This document contains the totality of the analysis I am carrying out on the first experiment of my first Qualifying Paper towards the PhD in Linguistics at Stanford University. This project deals with the processing of gendered and gender-neutral personal and professional titles. This is also being developed as my analysis portion of the final project in Judith Degen’s Methods in Psycholinguistics class (LING245B).

1 Preliminaries

1.1 Import Necessary Libraries & Source Files


We require the following libraries, and I’ve included their functions in my analysis below:

library(ggplot2) 
library(tidyverse) 
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
── Attaching packages ──────────────────────────────────────────────────────────────── tidyverse 1.3.1 ──
✓ tibble  3.1.2     ✓ dplyr   1.0.6
✓ tidyr   1.1.3     ✓ stringr 1.4.0
✓ readr   1.4.0     ✓ forcats 0.5.1
✓ purrr   0.3.4     
── Conflicts ─────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
x dplyr::filter() masks stats::filter()
x dplyr::lag()    masks stats::lag()
library(lme4) 
Loading required package: Matrix

Attaching package: ‘Matrix’

The following objects are masked from ‘package:tidyr’:

    expand, pack, unpack
library(stringr)
library(languageR)
library(lmerTest)

Attaching package: ‘lmerTest’

The following object is masked from ‘package:lme4’:

    lmer

The following object is masked from ‘package:stats’:

    step
library(reshape2)

Attaching package: ‘reshape2’

The following object is masked from ‘package:tidyr’:

    smiths
source("helpers.R") 
source("merge_results.R")
[1] "Not enough arguments supplied!"

ggplot2: data visualization
tidyverse: data management & manipulation
lme4: mixed-effects models
stringr: needed to compute string lengths

We also add “helpers.R” as a dependency, which includes custom code for implementing error bars in ggplot2 visualizations.

1.2 Set Gloabal ggplot2 Theme


This just makes it so that all ggplot2 plots will have the black & white theme:

theme_set(theme_minimal())

2 Data Importing & Management

Rscript merge_results.R gender_processing_a_selfpaced_reading_time_study_merged.csv mixed gender_processing_followup_demographics_merged.csv republican

2.1 Import Dataset


This reads in our .csv file with all 37,800 data points logged during the experiment.

all_data <- read.csv('merged_all.csv') %>%
  select(-error)

all_data %>%
  head()

2.2 Data-Point Exclusions

However, we definitely don’t want all 37,800 data points! Many of these aren’t critical trials, and this still includes the example trial where participants were learning how to take the experiment. In this section we filter out:

  • Example trials
  • Non-critical regions of the sentences
  • Participants who failed to reach the 85% accuracy threshold on the attention-check questions

Once we do this, we are left with a total of 3,720 data points!

This part is a bit ugly, so I’ve collapsed each of these into a separate tab, but absolutely free to explore if you’d like! They are (hopefully) thoroughly annotated.

2.2.1 Removing Example Trials


This simply filters out any data point which has the trial_id ‘example’, which is all 8 data points of the example trial, for each of the 200 participants.

all_data <- all_data %>% 
  filter(trial_id!= 'example')

all_data %>% 
  select(workerid,rt,trial_id)

2.2.2 Filter out non-critical data points


Similar to the previous tab (Removing Example Trials), this code removes from our data frame any non-critical data points. In this case, we are interested on the reading times on the personal and professional titles, so these have been logged as the ‘critical’ trials in the experiment. This code gets rid of everything else.

all_data <- all_data %>%
  filter(region=='critical')

all_data %>% 
  select(workerid,rt,region)

2.2.3 Running Exclusion Criteria


This is the most complicated of the removal steps, so let’s break it down.

First, we need to create a data frame with the workerids of every participant who did not meet the 85% accuracy threshold on the attention checks. The attention checks (logged as response_correct) have binary values: a 1 means that they got the question correct, while a zero (0) indicates that they did not.

So, we create a data frame with the participants who didn’t meet the threshold by grouping all the data by participant, and then creating a small data frame with just the workers and their relative accuracies, which is recorded in the new accuracy column. This is valued by meaning each participant’s response_correct scores:

exclusion <- all_data %>% group_by(workerid,system.Browser) %>%
  summarise(accuracy = mean(response_correct)) 
`summarise()` has grouped output by 'workerid'. You can override using the `.groups` argument.
exclusion


Now we can add to this another column, called exclude, which will assign them a value of ‘Yes’ if their data needs to be excluded based on their accuracy. If they scored over 85% accuracy, then they do not need to be excluded, so their exclude value is ‘No’.

exclusion <- exclusion %>%
  mutate(exclude = ifelse(accuracy < 0.85,'Yes','No'))

exclusion


Now we want to just get a list of all the participants who have been assigned a ‘Yes’ value, in order to eventually remove these participants from all the data. This code does that.

exclusion = exclusion %>% 
  filter(exclude == 'Yes') %>%
  select(workerid,system.Browser)

exclusion


Now, finally, we can exclude these participants! We do this by redefining all_data as itself, but without those rows which have workerid values appear in the list we just made in the above step!

all_data <- all_data[!(all_data$workerid %in% exclusion$workerid),]

all_data %>% 
  select(workerid,rt,trial_id)


If we want to check that the right number was subtracted, we can get the length of the first list we made (exclusion) and see if that matches the difference between the number of unique participants in our new all_data data frame and the original one (which had 200 to begin with). To do this we get the length of the list of unique worker ids in the all_data data frame, and subtract it from 200. Then we compare this to the length of the exclusion list.

298 - length(unique(all_data$workerid))
[1] 19
length(unique(exclusion$workerid))
[1] 19


If we’re being really extra, we can also run this as an equivalency to get a boolean True/False value:

298 - length(unique(all_data$workerid)) == length(unique(exclusion$workerid))
[1] TRUE

We can also filter out any trials which fall without 2.5 standard deviations for that trial.

all_data <- all_data %>%
  group_by(trial_id) %>%
  mutate(id_mean = mean(log(rt))) %>%
  mutate(exclusion = (log(rt) < mean(log(rt)) - 2*sd(log(rt))|(log(rt) > mean(log(rt)) + 2*sd(log(rt))))) %>%
  ungroup() %>%
  filter(exclusion==FALSE)

2.3 Adding in Additional Important Trial Information

Now that we have only the rows we want, let’s add some new columns, which will contain important information for each data point. Here, we will be adding:

  • Gender Ideology Subscores
  • Trial Genders
  • Trial Morphology Types
  • Critical Item Length
  • Trial Congruency

Ideally, I would’ve added all of these but the first when I actually created the stimuli and logged responses, but I forgot to! Luckily, R allows us to do this post-hoc fairly straightforwardly… which is good, since these features will be critical in our data visualization and analysis.

Again, some of this code is fairly ugly and involved, or irrelevant, so I’ve once again divvied it up into individual tabs, which you’re free to peruse or not.

2.3.1 Defining Gender Subscores


The question under investigation here is whether or not individuals’ conceptions of gender affect how they process gendered and gender-neutral forms of English personal and professional titles.

In order to examine this, we need to quanify participants’ ideological views! Here we have adopted the 13-item Social Roles Questionnaire put forth in Baber & Tucker (2006). Questions 1-5 correlate to the ‘Gender Transcendent’ subscale, and questions 6-13 correspond to the ‘Gender Linked’ subscale. Each item is scored on a scale of 0-100. So, the first thing we want to do is make two lists of columns which correspond to these two subscales, since the questions are stored individually in the data:

gender_transcendence_cols <- c('subject_information.gender_q1','subject_information.gender_q2','subject_information.gender_q3','subject_information.gender_q4','subject_information.gender_q5')

gender_linked_cols <- c('subject_information.gender_q6','subject_information.gender_q7','subject_information.gender_q8','subject_information.gender_q9','subject_information.gender_q10','subject_information.gender_q11','subject_information.gender_q12','subject_information.gender_q13')


Now we can use the mutate() method on all_data to add two new columns, one for each subscale. We tell R to take the means of the specified columns in [column_names] of all_data for each individual row: rowMeans(all_data[column_names]).

We also have to subtract this mean from 100 in the case of the ‘Gender Transcendent’ subscale, since it is inversely scored. This is easy enough to do during the mutation step:

all_data <- all_data %>%
  mutate(gender_trans = 100 - (rowMeans(all_data[gender_transcendence_cols]))) %>%
  mutate(gender_link = rowMeans(all_data[gender_linked_cols]))

all_data %>%
  select(workerid,rt,gender_trans,gender_link) 


Finally, we probably want something that includes the average across all the gender questions, regardless of subscores. This is easy enough, since we just have to mean the two subscores we already made. So, let’s define a column list:

gender_all = c('gender_trans','gender_link')


Now we mutate a new column!

all_data <- all_data %>%
  mutate(gender_total = rowMeans(all_data[gender_all]))

all_data %>%
  select(workerid,rt,gender_trans,gender_link,gender_total) 

2.3.2 Adding Gender


We also want to add whether the trial included a female or male referent (but also, like, destroy the binary!). In order to do this, we’ll just add a trial_gender column that says ‘female’ if the condition was either ‘neutral_female’ or ‘congruent_female’. Otherwise, we want the trial_gender to say ‘male’.

all_data <- all_data %>%
  mutate(trial_gender = ifelse(condition=='neutral_female' | condition == 'congruent_female','female','male'))

all_data %>%
  select(workerid,rt,condition,trial_id,trial_gender)

2.3.3 Adding Morphology Type


Now we want to add whether or not the lexeme’s neutral form is developed by compounding (as in ‘congress-person’) or by the adoption of the male form (as in ‘actor’ being used more for both men and women). In this study, we only have six lexemes of the latter type, so we’ll just tell R to assign those a morph_type value of ‘adoption’ (for ‘male adoption’), and all else will be assigned a value of ‘compound’.

all_data <- all_data%>%
  mutate(morph_type = ifelse(lexeme!= 'actor' & lexeme!= 'host' & lexeme !='hunter' & lexeme!= 'villain' & lexeme!= 'heir' & lexeme!= 'hero','compound','adoption'))

all_data %>%
  select(rt,lexeme,morph_type)

2.3.4 Adding Form Length


Another important factor we want to explore is the length of the critical item! In order to add this, we simply create a new column form_length and tell R to input as that column’s value the length of the string that appears in that row’s form column, which corresponds to the orthograpic form of the critical item in that trial. Note that this will include spaces in the count!

all_data <- all_data %>%
  mutate(form_length = str_length(form))

all_data %>%
  select(rt,lexeme,form,form_length)

2.3.5 Adding Congruency Column


Finally, let’s make sure we have a column which records whether or not the trial was gender-congruent (as in ‘Shelby is a congresswoman’) or gender neutral (as in ‘Shelby is a congressperson’). We add a trial_congruency column, which is valued as ‘congruent’ if that row’s condition is one of the two congruent conditions. Otherwise, it gets valued as ‘neutral’.

all_data <- all_data %>%
  mutate(trial_congruency = ifelse(condition=='congruent_male' | condition == 'congruent_female','congruent','neutral'))

all_data %>%
  select(rt,condition,trial_congruency)

2.3.5.1 Adding Reading Time Residuals

simple_model <- lm(log(rt)~form_length, data = all_data)
summary(simple_model)

Call:
lm(formula = log(rt) ~ form_length, data = all_data)

Residuals:
    Min      1Q  Median      3Q     Max 
-1.4812 -0.2896 -0.0084  0.3201  1.8782 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) 5.997374   0.020123  298.04   <2e-16 ***
form_length 0.026594   0.002058   12.92   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.4919 on 5340 degrees of freedom
Multiple R-squared:  0.03031,   Adjusted R-squared:  0.03013 
F-statistic: 166.9 on 1 and 5340 DF,  p-value: < 2.2e-16
all_data <- all_data %>%
  mutate(resid_rt = resid(simple_model))
ggplot(data=all_data, aes(x=log(rt),y=resid_rt)) + 
  geom_point()

2.3.6 Adding Large Political Macrocategories

all_data <- all_data %>%
  mutate(poli_party = ifelse(subject_information.party_alignment == 1 | subject_information.party_alignment == 2,'Republican',ifelse(subject_information.party_alignment == 4 | subject_information.party_alignment == 5,'Democrat','Non-Partisan')))

2.4 Review summary with new columns


Now we can use the head() method to check the current state of the data frame, which should include a grand total of 46 columns!

head(all_data)


And now, our data should be completely ready to visualize and analyze!

3 Data Visualization

[THIS PART UNDER CONSTRUCTION: COMING SOON]

Now that we have our data ready for visualization and analysis, let’s do the former!

3.1 A Preliminary

inauguration_2021 = c("#5445b1", "#749dae", "#f3c483", "#5c1a33", "#cd3341","#f7dc6a")
cbPalette <- c("#999999", "#E69F00", "#56B4E9", "#009E73", "#F0E442", "#0072B2", "#D55E00", "#CC79A7")

3.2 Plots

3.2.1 Log Reading, non-residual

ggplot(all_data, aes(x=rt, fill=morph_type)) + 
  geom_density(alpha=.6) + 
  labs(x="Raw Reading Time", y="Density",fill="Critical Item Morphology Type") + 
  scale_fill_manual(values = cbPalette)

 ggsave("plots/morph_type_density.jpg",height=5,width=8)
ggplot(all_data, aes(x=rt, fill=condition)) + 
  geom_density(alpha=.4) + 
  labs(x="Raw Reading Time", y="Density",fill="Critical Item Condition") + 
  scale_fill_manual(values = cbPalette)

ggplot(all_data, aes(x=rt, fill=trial_gender)) + 
  geom_density(alpha=.6) + 
  labs(x="Raw Reading Time", y="Density",fill="Critical Item Gender") + 
  scale_fill_manual(values = cbPalette)

ggplot(all_data, aes(x=rt, fill=trial_congruency)) + 
  geom_density(alpha=.6) + 
  labs(x="Raw Reading Time", y="Density",fill="Critical Item Congruency") + 
  scale_fill_manual(values = cbPalette)

ggplot(all_data, aes(x=gender_total, y=rt)) + 
  geom_point(alpha=.5) + 
  geom_smooth(method = 'lm', size=1.2)

all_data %>%
  group_by(subject_information.party_alignment,condition) %>%
  summarize(MeanRT = mean(rt), CI.Low = ci.low(rt), CI.High = ci.high(rt)) %>%
  mutate(YMin = MeanRT - CI.Low, YMax = MeanRT + CI.High) %>%
  ggplot(aes(x=subject_information.party_alignment,y=MeanRT,fill=subject_information.party_alignment)) + 
  geom_bar(stat='identity') + 
  theme(legend.position = "none") + 
  geom_errorbar(aes(ymin=YMin,ymax=YMax),width=.25) + 
  facet_wrap(~ condition)
`summarise()` has grouped output by 'subject_information.party_alignment'. You can override using the `.groups` argument.

ggplot(all_data, aes(x=form_length,y=rt)) + 
  geom_point()

agr <- all_data %>%
  group_by(trial_gender,trial_congruency) %>%
  summarize(MeanRT = mean(rt), CI.Low = ci.low(rt), CI.High = ci.high(rt)) %>%
  mutate(YMin = MeanRT - CI.Low, YMax = MeanRT + CI.High)
`summarise()` has grouped output by 'trial_gender'. You can override using the `.groups` argument.
dodge = position_dodge(.9)
ggplot(data=agr, aes(x=trial_gender,y=MeanRT,fill=trial_congruency)) + 
  geom_bar(stat='identity',position=dodge) + 
  geom_errorbar(aes(ymin=YMin,ymax=YMax),width=.25,position=dodge)

temp <- all_data %>%
  group_by(trial_gender) %>%
  summarize(MeanRT = mean(rt), CI.Low = ci.low(rt), CI.High = ci.high(rt)) %>%
  mutate(YMin = MeanRT - CI.Low, YMax = MeanRT + CI.High)

dodge = position_dodge(.9)
ggplot(data=temp, aes(x=trial_gender,y=MeanRT,fill=trial_gender)) + 
  geom_bar(stat='identity',position=dodge) + 
  geom_errorbar(aes(ymin=YMin,ymax=YMax),width=.25,position=dodge) +
  theme(legend.position = 'none')

all_data %>%
ggplot(aes(x=morph_type, y=rt)) + 
  geom_boxplot()

agg_speaker_mean_length <- all_data %>%
  group_by(form_length,workerid) %>%
  summarize(MeanRT=mean(rt))
`summarise()` has grouped output by 'form_length'. You can override using the `.groups` argument.
all_data %>%
  group_by(form_length) %>%
  summarize(MeanRT = mean(rt), CI.Low = ci.low(rt), CI.High = ci.high(rt)) %>%
  mutate(YMin = MeanRT - CI.Low, YMax = MeanRT + CI.High) %>%
  ggplot(aes(x=form_length,y=MeanRT)) + 
  geom_col() + 
  geom_jitter(data = agg_speaker_mean_length, aes(y=MeanRT),alpha=.2,color='blue') + 
  geom_errorbar(aes(ymin=YMin,ymax=YMax), width=.25) + 
  geom_smooth(method = 'lm', size=1.2) 

all_data %>%
  ggplot(aes(x=form_length, y=rt,color=morph_type)) + 
  geom_jitter() + 
  geom_smooth(method = 'lm', size=1.2) 

all_data %>%
  ggplot(aes(x=log(gender_trans), y=resid_rt,color=morph_type)) + 
  geom_jitter() + 
  geom_smooth(method = 'lm', size=1.2) 

all_data %>%
  ggplot(aes(x=subject_information.age, y=resid_rt,color=morph_type)) + 
  geom_jitter() + 
  geom_smooth(method = 'lm', size=1.2) 

all_data %>%
  filter(!is.na(poli_party)) %>%
  group_by(workerid,subject_information.education) %>%
  ggplot(aes(x=subject_information.education)) + 
  geom_bar() + 
  facet_wrap(~poli_party)

3.2.2 Residual Analyses

agg_speaker_mean <- all_data %>%
  group_by(morph_type,workerid) %>%
  summarize(MeanRT=mean(resid_rt))
`summarise()` has grouped output by 'morph_type'. You can override using the `.groups` argument.
all_data %>%
  group_by(morph_type) %>%
  summarize(MeanRT = mean(resid_rt), CI.Low = ci.low(resid_rt), CI.High = ci.high(resid_rt)) %>%
  mutate(YMin = MeanRT - CI.Low, YMax = MeanRT + CI.High) %>%
  ggplot(aes(x=morph_type,y=MeanRT)) + 
  geom_point(size=3) + 
  geom_jitter(data = agg_speaker_mean, aes(y=MeanRT),alpha=.2,color='blue') + 
  geom_errorbar(aes(ymin=YMin,ymax=YMax), width=.25)

agg_speaker_mean_con <- all_data %>%
  group_by(condition,workerid) %>%
  summarize(MeanRT=mean(resid_rt))
`summarise()` has grouped output by 'condition'. You can override using the `.groups` argument.
all_data %>%
  group_by(condition,trial_gender) %>%
  summarize(MeanRT = mean(resid_rt), CI.Low = ci.low(resid_rt), CI.High = ci.high(resid_rt)) %>%
  mutate(YMin = MeanRT - CI.Low, YMax = MeanRT + CI.High) %>%
  ggplot(aes(x=condition,y=MeanRT,color=trial_gender)) + 
  geom_point(size=3) + 
  geom_jitter(data = agg_speaker_mean_con, aes(y=MeanRT),alpha=.2,color='mediumslateblue') + 
  geom_errorbar(aes(ymin=YMin,ymax=YMax), width=.25) + 
  scale_color_manual(values = cbPalette)
`summarise()` has grouped output by 'condition'. You can override using the `.groups` argument.

all_data %>%
  group_by(condition,trial_gender,trial_congruency,lexeme) %>%
  summarize(MeanRT = mean(resid_rt), CI.Low = ci.low(resid_rt), CI.High = ci.high(resid_rt)) %>%
  mutate(YMin = MeanRT - CI.Low, YMax = MeanRT + CI.High) %>%
  ggplot(aes(x=condition,y=MeanRT,color=trial_gender,shape=trial_congruency)) + 
  geom_point(size=3) +
  geom_errorbar(aes(ymin=YMin,ymax=YMax), width=.25) + 
  facet_wrap(~ lexeme) +
  theme(axis.text.x = element_text(angle = 45, vjust = .7, hjust=.7)) + 
  scale_color_manual(values = cbPalette)
`summarise()` has grouped output by 'condition', 'trial_gender', 'trial_congruency'. You can override using the `.groups` argument.

temp <- all_data %>%
  group_by(lexeme,trial_gender,trial_congruency) %>%
  summarize(meanRT = mean(resid_rt)) %>%
  spread(trial_congruency,meanRT) %>%
  mutate(con_dif = neutral-congruent)
`summarise()` has grouped output by 'lexeme', 'trial_gender'. You can override using the `.groups` argument.
ggplot(temp, aes(x=trial_gender,y=con_dif, fill=trial_gender)) +
  geom_bar(stat='identity') + 
  theme(legend.position = 'none') + 
  geom_hline(yintercept =0) + 
  facet_wrap(~lexeme) + 
  labs(x = 'Trial Gender', y='Congruency Difference (Neutral-Congruent)')

all_data %>%
  filter(lexeme=='firefighter') %>%
  group_by(trial_gender) %>%
  summarize(MeanRT = mean(resid_rt), CI.Low = ci.low(resid_rt), CI.High = ci.high(resid_rt)) %>%
  mutate(YMin = MeanRT - CI.Low, YMax = MeanRT + CI.High) %>%
  ggplot(aes(x=trial_gender,y=MeanRT,color=trial_gender)) + 
  geom_point(size=3) +
  geom_errorbar(aes(ymin=YMin,ymax=YMax), width=.25)

ggplot(all_data, aes(x=trial_gender,y=resid_rt)) + 
  geom_bar(stat='identity') + 
  facet_wrap(~lexeme)

 ggsave("plots/difference-plot.jpg",height=5,width=8)
all_data %>%
  filter(!is.na(poli_party)) %>%
  group_by(poli_party,condition,trial_gender,trial_congruency) %>%
  summarize(MeanRT = mean(resid_rt), CI.Low = ci.low(resid_rt), CI.High = ci.high(resid_rt)) %>%
  mutate(YMin = MeanRT - CI.Low, YMax = MeanRT + CI.High) %>%
  ggplot(aes(x=condition,y=MeanRT,color=trial_gender,shape=trial_congruency)) + 
  geom_point(size=3) +
  geom_errorbar(aes(ymin=YMin,ymax=YMax), width=.25) + 
  facet_wrap(~ poli_party, nrow = 1) +
  theme(axis.text.x = element_text(angle = 45, vjust = .7, hjust=.7)) + 
  scale_color_manual(values = cbPalette)
`summarise()` has grouped output by 'poli_party', 'condition', 'trial_gender'. You can override using the `.groups` argument.

all_data %>%
  filter(!is.na(subject_information.party_alignment)) %>%
  group_by(subject_information.party_alignment,condition,trial_gender,trial_congruency) %>%
  summarize(MeanRT = mean(resid_rt), CI.Low = ci.low(resid_rt), CI.High = ci.high(resid_rt)) %>%
  mutate(YMin = MeanRT - CI.Low, YMax = MeanRT + CI.High) %>%
  ggplot(aes(x=condition,y=MeanRT,color=trial_gender,shape=trial_congruency)) + 
  geom_point(size=3) +
  geom_errorbar(aes(ymin=YMin,ymax=YMax), width=.25) + 
  facet_wrap(~ subject_information.party_alignment, nrow = 1) +
  theme(axis.text.x = element_text(angle = 45, vjust = .7, hjust=.7)) + 
  scale_color_manual(values = cbPalette)
`summarise()` has grouped output by 'subject_information.party_alignment', 'condition', 'trial_gender'. You can override using the `.groups` argument.

aggr_speaker <- all_data %>%
  group_by(gender_link,workerid,trial_gender,trial_congruency) %>%
  summarise(meanrt = mean(resid_rt))
`summarise()` has grouped output by 'gender_link', 'workerid', 'trial_gender'. You can override using the `.groups` argument.
  
aggr_speaker %>%
  ggplot(aes(x=gender_link,y=meanrt,color=trial_gender,linetype=trial_congruency)) + 
  geom_point() + 
  geom_smooth(method='lm')

agg_speaker_mean_gen <- all_data %>%
  group_by(trial_gender,workerid) %>%
  summarize(MeanRT=mean(resid_rt))
`summarise()` has grouped output by 'trial_gender'. You can override using the `.groups` argument.
all_data %>%
  group_by(trial_gender) %>%
  summarize(MeanRT = mean(resid_rt), CI.Low = ci.low(resid_rt), CI.High = ci.high(resid_rt)) %>%
  mutate(YMin = MeanRT - CI.Low, YMax = MeanRT + CI.High) %>%
  ggplot(aes(x=trial_gender,y=MeanRT)) + 
  geom_point(size=3) + 
  geom_jitter(data = agg_speaker_mean_gen, aes(y=MeanRT),alpha=.4,color='mediumslateblue') + 
  geom_errorbar(aes(ymin=YMin,ymax=YMax), width=.25) 

all_data %>%
  filter(!is.na(poli_party)) %>%
  group_by(trial_congruency,poli_party) %>%
  summarize(MeanRT = mean(resid_rt), CI.Low = ci.low(resid_rt), CI.High = ci.high(resid_rt)) %>%
  mutate(YMin = MeanRT - CI.Low, YMax = MeanRT + CI.High) %>%
  ggplot(aes(x=trial_congruency,y=MeanRT)) + 
  geom_point(size=3) + 
  geom_errorbar(aes(ymin=YMin,ymax=YMax), width=.25) + 
  facet_wrap(~poli_party)
`summarise()` has grouped output by 'trial_congruency'. You can override using the `.groups` argument.

all_data %>%
  filter(!is.na(poli_party)) %>%
  group_by(trial_gender,poli_party) %>%
  summarize(MeanRT = mean(resid_rt), CI.Low = ci.low(resid_rt), CI.High = ci.high(resid_rt)) %>%
  mutate(YMin = MeanRT - CI.Low, YMax = MeanRT + CI.High) %>%
  ggplot(aes(x=trial_gender,y=MeanRT)) + 
  geom_point(size=3) + 
  geom_errorbar(aes(ymin=YMin,ymax=YMax), width=.25) + 
  facet_wrap(~poli_party)
`summarise()` has grouped output by 'trial_gender'. You can override using the `.groups` argument.

poli_data <- all_data %>% 
  group_by(workerid) %>%
  summarise(party = paste(unique(poli_party)))
table(poli_data$party)

    Democrat           NA Non-Partisan   Republican 
         113            2           60          103 
poli_data_gran <- all_data %>% 
  group_by(workerid) %>%
  summarise(party = paste(unique(subject_information.party_alignment)))
table(poli_data_gran$party)

 1  2  3  4  5 NA 
27 76 60 60 53  2 

4 Model Analyses

4.1 Reading Time

all_data <- all_data %>%
  mutate(ctrial_congruency = as.numeric(as.factor(trial_congruency))-mean(as.numeric(as.factor(trial_congruency)))) %>%
  mutate(ctrial_gender = as.numeric(as.factor(trial_gender))-mean(as.numeric(as.factor(trial_gender)))) %>%
  mutate(cgender_link = scale(gender_link))
complex_model <- lmer(resid_rt~ctrial_congruency*ctrial_gender*cgender_link + (1|workerid) + (1|lexeme) + (1|name),data = all_data,control=lmerControl(optCtrl=list(maxfun=40000)))
summary(complex_model)
Linear mixed model fit by REML. t-tests use Satterthwaite's method ['lmerModLmerTest']
Formula: resid_rt ~ ctrial_congruency * ctrial_gender * cgender_link +  
    (1 | workerid) + (1 | lexeme) + (1 | name)
   Data: all_data
Control: lmerControl(optCtrl = list(maxfun = 40000))

REML criterion at convergence: 3821

Scaled residuals: 
    Min      1Q  Median      3Q     Max 
-3.9473 -0.6006 -0.0489  0.5240  4.9953 

Random effects:
 Groups   Name        Variance Std.Dev.
 workerid (Intercept) 0.149388 0.38651 
 name     (Intercept) 0.000236 0.01536 
 lexeme   (Intercept) 0.001422 0.03771 
 Residual             0.098718 0.31419 
Number of obs: 5342, groups:  workerid, 278; name, 24; lexeme, 20

Fixed effects:
                                               Estimate Std. Error         df t value Pr(>|t|)   
(Intercept)                                   1.989e-03  2.524e-02  2.558e+02   0.079  0.93724   
ctrial_congruency                            -2.719e-03  8.622e-03  5.041e+03  -0.315  0.75247   
ctrial_gender                                -3.443e-02  1.067e-02  2.215e+01  -3.227  0.00385 **
cgender_link                                 -5.114e-02  2.359e-02  2.740e+02  -2.168  0.03101 * 
ctrial_congruency:ctrial_gender               4.703e-02  1.724e-02  5.040e+03   2.728  0.00640 **
ctrial_congruency:cgender_link               -1.512e-03  8.634e-03  5.048e+03  -0.175  0.86101   
ctrial_gender:cgender_link                   -5.162e-03  8.626e-03  5.031e+03  -0.598  0.54955   
ctrial_congruency:ctrial_gender:cgender_link  7.885e-03  1.726e-02  5.045e+03   0.457  0.64776   
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Correlation of Fixed Effects:
                 (Intr) ctrl_c ctrl_g cgndr_ ctrl_cngrncy:ct_ ctrl_cngrncy:cg_ ctrl_g:_
ctrl_cngrnc       0.000                                                                
ctrial_gndr       0.000 -0.002                                                         
cgender_lnk       0.000  0.000 -0.001                                                  
ctrl_cngrncy:ct_  0.000  0.000  0.000  0.001                                           
ctrl_cngrncy:cg_  0.000 -0.001  0.003  0.000 -0.005                                    
ctrl_gndr:_      -0.001  0.003  0.001 -0.001  0.003           -0.001                   
ctrl_cn:_:_       0.000 -0.005  0.003  0.000 -0.001           -0.001           -0.001  
plot(complex_model)

complex_model_bare <- lmer(resid_rt~trial_congruency*trial_gender + (1 + trial_congruency + trial_gender | workerid) + (1|lexeme) + (1|name),data = all_data,control=lmerControl(optCtrl=list(maxfun=20000)))
Model failed to converge with max|grad| = 0.00547368 (tol = 0.002, component 1)
summary(complex_model_bare)
Linear mixed model fit by REML. t-tests use Satterthwaite's method ['lmerModLmerTest']
Formula: resid_rt ~ trial_congruency * trial_gender + (1 + trial_congruency +  
    trial_gender | workerid) + (1 | lexeme) + (1 | name)
   Data: all_data
Control: lmerControl(optCtrl = list(maxfun = 20000))

REML criterion at convergence: 3794.4

Scaled residuals: 
    Min      1Q  Median      3Q     Max 
-3.9481 -0.5975 -0.0524  0.5258  5.0348 

Random effects:
 Groups   Name                    Variance  Std.Dev. Corr       
 workerid (Intercept)             0.1557168 0.39461             
          trial_congruencyneutral 0.0012882 0.03589   0.00      
          trial_gendermale        0.0024540 0.04954  -0.22 -0.88
 name     (Intercept)             0.0002237 0.01496             
 lexeme   (Intercept)             0.0014150 0.03762             
 Residual                         0.0977015 0.31257             
Number of obs: 5342, groups:  workerid, 278; name, 24; lexeme, 20

Fixed effects:
                                           Estimate Std. Error         df t value Pr(>|t|)    
(Intercept)                                 0.03257    0.02691  270.59332   1.210  0.22715    
trial_congruencyneutral                    -0.02639    0.01236  930.63983  -2.135  0.03304 *  
trial_gendermale                           -0.05809    0.01392   64.26563  -4.172 9.23e-05 ***
trial_congruencyneutral:trial_gendermale    0.04719    0.01715 4509.61144   2.751  0.00597 ** 
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Correlation of Fixed Effects:
            (Intr) trl_cn trl_gn
trl_cngrncy -0.222              
tril_gndrml -0.290  0.397       
trl_cngrn:_  0.161 -0.699 -0.618
optimizer (nloptwrap) convergence code: 0 (OK)
Model failed to converge with max|grad| = 0.00547368 (tol = 0.002, component 1)
plot(complex_model)

complex_model_antirandom <- lm(resid_rt~ctrial_congruency*ctrial_gender*cgender_link, data = all_data)
plot(complex_model_antirandom)

5 Extras

5.1 Confirming the Gender Scales

no_genderless <- all_data[complete.cases(all_data), ]

gender_check <- lm(gender_total~subject_information.gender*poli_party, data=no_genderless)
summary(gender_check)

Call:
lm(formula = gender_total ~ subject_information.gender * poli_party, 
    data = no_genderless)

Residuals:
    Min      1Q  Median      3Q     Max 
-31.805  -9.193  -0.716   6.994  48.282 

Coefficients: (4 not defined because of singularities)
                                                        Estimate Std. Error t value Pr(>|t|)    
(Intercept)                                              16.7206     2.3899   6.996 2.96e-12 ***
subject_information.genderFemale                         -4.2548     2.4184  -1.759  0.07858 .  
subject_information.genderMale                            4.8015     2.3491   2.044  0.04101 *  
subject_information.genderOther                          -7.5833     2.9816  -2.543  0.01101 *  
poli_partyNon-Partisan                                   12.0707     0.7416  16.276  < 2e-16 ***
poli_partyRepublican                                     16.4216     0.5906  27.803  < 2e-16 ***
subject_information.genderFemale:poli_partyNon-Partisan  -0.7917     0.9746  -0.812  0.41665    
subject_information.genderMale:poli_partyNon-Partisan         NA         NA      NA       NA    
subject_information.genderOther:poli_partyNon-Partisan        NA         NA      NA       NA    
subject_information.genderFemale:poli_partyRepublican    -2.4439     0.8370  -2.920  0.00352 ** 
subject_information.genderMale:poli_partyRepublican           NA         NA      NA       NA    
subject_information.genderOther:poli_partyRepublican          NA         NA      NA       NA    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 13.1 on 5255 degrees of freedom
Multiple R-squared:  0.331, Adjusted R-squared:  0.3301 
F-statistic: 371.5 on 7 and 5255 DF,  p-value: < 2.2e-16
plot(gender_check)

5.2 Comments

summary(all_data$subject_information.comments)
   Length     Class      Mode 
     5342 character character 
all_data %>%
  group_by(workerid,poli_party) %>%
  summarise(comments = paste(unique(subject_information.comments))) %>%
  select(poli_party,comments)
`summarise()` has grouped output by 'workerid'. You can override using the `.groups` argument.
Adding missing grouping variables: `workerid`
age_model <- lm(resid_rt~subject_information.age, data = all_data)
summary(age_model)

Call:
lm(formula = resid_rt ~ subject_information.age, data = all_data)

Residuals:
     Min       1Q   Median       3Q      Max 
-1.45805 -0.29057 -0.00589  0.30047  1.92381 

Coefficients:
                          Estimate Std. Error t value Pr(>|t|)    
(Intercept)             -0.3355903  0.0191637  -17.51   <2e-16 ***
subject_information.age  0.0099982  0.0005367   18.63   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.4768 on 5300 degrees of freedom
  (40 observations deleted due to missingness)
Multiple R-squared:  0.06146,   Adjusted R-squared:  0.06128 
F-statistic: 347.1 on 1 and 5300 DF,  p-value: < 2.2e-16
new_toy %>%
  filter(proliferate.condition=="all") %>%
  summary()
    workerid     proliferate.condition  condition             form              lexeme         
 Min.   :125.0   Length:37800          Length:37800       Length:37800       Length:37800      
 1st Qu.:179.5   Class :character      Class :character   Class :character   Class :character  
 Median :234.5   Mode  :character      Mode  :character   Mode  :character   Mode  :character  
 Mean   :234.5                                                                                 
 3rd Qu.:289.2                                                                                 
 Max.   :341.0                                                                                 
                                                                                               
     name              region          response_correct       rt            trial_id        
 Length:37800       Length:37800       Min.   :0.0000   Min.   :    2.0   Length:37800      
 Class :character   Class :character   1st Qu.:1.0000   1st Qu.:  319.0   Class :character  
 Mode  :character   Mode  :character   Median :1.0000   Median :  412.0   Mode  :character  
                                       Mean   :0.9545   Mean   :  514.4                     
                                       3rd Qu.:1.0000   3rd Qu.:  556.0                     
                                       Max.   :1.0000   Max.   :39051.0                     
                                                                                            
    trial_no     word_idx subject_information.age subject_information.asses subject_information.comments
 Min.   : 0   Min.   :0   Min.   :18.00           Length:37800              Length:37800                
 1st Qu.: 5   1st Qu.:2   1st Qu.:23.00           Class :character          Class :character            
 Median :10   Median :4   Median :30.00           Mode  :character          Mode  :character            
 Mean   :10   Mean   :4   Mean   :32.69                                                                 
 3rd Qu.:15   3rd Qu.:6   3rd Qu.:36.00                                                                 
 Max.   :20   Max.   :8   Max.   :74.00                                                                 
                          NA's   :378                                                                   
 subject_information.education subject_information.enjoyment subject_information.gender
 Min.   :-1.00                 Min.   :0.00                  Length:37800              
 1st Qu.: 2.00                 1st Qu.:1.00                  Class :character          
 Median : 3.00                 Median :2.00                  Mode  :character          
 Mean   : 2.54                 Mean   :1.67                                            
 3rd Qu.: 3.00                 3rd Qu.:2.00                                            
 Max.   : 4.00                 Max.   :2.00                                            
                                                                                       
 subject_information.gender_q1 subject_information.gender_q10 subject_information.gender_q11
 Min.   :  0.00                Min.   :  0.00                 Min.   :  0.00                
 1st Qu.: 83.00                1st Qu.:  0.00                 1st Qu.:  0.00                
 Median : 97.00                Median : 12.50                 Median : 28.50                
 Mean   : 89.84                Mean   : 21.07                 Mean   : 32.72                
 3rd Qu.:100.00                3rd Qu.: 36.00                 3rd Qu.: 59.00                
 Max.   :100.00                Max.   :100.00                 Max.   :100.00                
                                                                                            
 subject_information.gender_q12 subject_information.gender_q13 subject_information.gender_q2
 Min.   :  0.0                  Min.   :  0.00                 Min.   : 20.00               
 1st Qu.:  0.0                  1st Qu.:  0.00                 1st Qu.: 90.00               
 Median : 15.5                  Median :  1.00                 Median :100.00               
 Mean   : 26.1                  Mean   : 14.17                 Mean   : 91.98               
 3rd Qu.: 51.0                  3rd Qu.: 19.00                 3rd Qu.:100.00               
 Max.   :100.0                  Max.   :100.00                 Max.   :100.00               
                                                                                            
 subject_information.gender_q3 subject_information.gender_q4 subject_information.gender_q5
 Min.   :  1.00                Min.   :  0.00                Min.   :  0.00               
 1st Qu.: 88.75                1st Qu.: 90.00                1st Qu.: 68.00               
 Median :100.00                Median :100.00                Median : 91.50               
 Mean   : 91.64                Mean   : 90.11                Mean   : 80.39               
 3rd Qu.:100.00                3rd Qu.:100.00                3rd Qu.:100.00               
 Max.   :100.00                Max.   :100.00                Max.   :100.00               
                                                                                          
 subject_information.gender_q6 subject_information.gender_q7 subject_information.gender_q8
 Min.   :  0.00                Min.   :  0.00                Min.   :  0.00               
 1st Qu.: 29.50                1st Qu.:  8.75                1st Qu.:  0.00               
 Median : 51.00                Median : 40.00                Median : 21.50               
 Mean   : 49.63                Mean   : 35.87                Mean   : 31.86               
 3rd Qu.: 69.25                3rd Qu.: 53.25                3rd Qu.: 59.25               
 Max.   :100.00                Max.   :100.00                Max.   :100.00               
                                                                                          
 subject_information.gender_q9 subject_information.language subject_information.party_alignment
 Min.   :  0.00                Length:37800                 Min.   :1.000                      
 1st Qu.:  1.50                Class :character             1st Qu.:3.000                      
 Median : 24.00                Mode  :character             Median :4.000                      
 Mean   : 27.84                                             Mean   :3.722                      
 3rd Qu.: 49.00                                             3rd Qu.:5.000                      
 Max.   :100.00                                             Max.   :5.000                      
                                                            NA's   :378                        
 subject_information.price system.Browser      system.OS         system.screenH   system.screenW
 Min.   :0.000             Length:37800       Length:37800       Min.   : 614.0   Min.   :1092  
 1st Qu.:1.000             Class :character   Class :character   1st Qu.: 800.0   1st Qu.:1366  
 Median :1.000             Mode  :character   Mode  :character   Median : 900.0   Median :1536  
 Mean   :0.965                                                   Mean   : 929.3   Mean   :1603  
 3rd Qu.:1.000                                                   3rd Qu.:1080.0   3rd Qu.:1920  
 Max.   :1.000                                                   Max.   :1440.0   Max.   :2560  
                                                                                                
 time_in_minutes   error         speaker_cond      
 Min.   : 2.181   Mode:logical   Length:37800      
 1st Qu.: 3.764   NA's:37800     Class :character  
 Median : 4.848                  Mode  :character  
 Mean   : 5.382                                    
 3rd Qu.: 6.122                                    
 Max.   :30.623                                    
                                                   
LS0tCnRpdGxlOiAiUHJvY2Vzc2luZyBvZiBHZW5kZXJlZCBhbmQgR2VuZGVyLU5ldXRyYWwgUHJvZmVzc2lvbmFsIGFuZCBQZXJzb25hbCBUaXRsZXMiCmF1dGhvcjogIkJyYW4gUGFwaW5lYXUiCmRhdGU6ICJNYXkgMjAyMSIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IG5vCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcwogICAgdGhlbWU6IGpvdXJuYWwKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIGRmX3ByaW50OiBwYWdlZAotLS0KCgpUaGlzIGRvY3VtZW50IGNvbnRhaW5zIHRoZSB0b3RhbGl0eSBvZiB0aGUgYW5hbHlzaXMgSSBhbSBjYXJyeWluZyBvdXQgb24gdGhlIGZpcnN0IGV4cGVyaW1lbnQgb2YgbXkgZmlyc3QgUXVhbGlmeWluZyBQYXBlciB0b3dhcmRzIHRoZSBQaEQgaW4gTGluZ3Vpc3RpY3MgYXQgU3RhbmZvcmQgVW5pdmVyc2l0eS4gVGhpcyBwcm9qZWN0IGRlYWxzIHdpdGggdGhlIHByb2Nlc3Npbmcgb2YgZ2VuZGVyZWQgYW5kIGdlbmRlci1uZXV0cmFsIHBlcnNvbmFsIGFuZCBwcm9mZXNzaW9uYWwgdGl0bGVzLiBUaGlzIGlzIGFsc28gYmVpbmcgZGV2ZWxvcGVkIGFzIG15IGFuYWx5c2lzIHBvcnRpb24gb2YgdGhlIGZpbmFsIHByb2plY3QgaW4gSnVkaXRoIERlZ2VuJ3MgTWV0aG9kcyBpbiBQc3ljaG9saW5ndWlzdGljcyBjbGFzcyAoTElORzI0NUIpLgoKIyBQcmVsaW1pbmFyaWVzCgojIyBJbXBvcnQgTmVjZXNzYXJ5IExpYnJhcmllcyAmIFNvdXJjZSBGaWxlcwo8YnI+CldlIHJlcXVpcmUgdGhlIGZvbGxvd2luZyBsaWJyYXJpZXMsIGFuZCBJJ3ZlIGluY2x1ZGVkIHRoZWlyIGZ1bmN0aW9ucyBpbiBteSBhbmFseXNpcyBiZWxvdzoKCmBgYHtyIGVjaG89VFJVRX0KbGlicmFyeShnZ3Bsb3QyKSAKbGlicmFyeSh0aWR5dmVyc2UpIApsaWJyYXJ5KGxtZTQpIApsaWJyYXJ5KHN0cmluZ3IpCmxpYnJhcnkobGFuZ3VhZ2VSKQpsaWJyYXJ5KGxtZXJUZXN0KQpsaWJyYXJ5KHJlc2hhcGUyKQoKc291cmNlKCJoZWxwZXJzLlIiKSAKc291cmNlKCJtZXJnZV9yZXN1bHRzLlIiKQpgYGAKCjxiPmdncGxvdDI8L2I+OiBkYXRhIHZpc3VhbGl6YXRpb24gPGJyPgo8Yj50aWR5dmVyc2U8L2I+OiBkYXRhIG1hbmFnZW1lbnQgJiBtYW5pcHVsYXRpb24gPGJyPgo8Yj5sbWU0PC9iPjogbWl4ZWQtZWZmZWN0cyBtb2RlbHMgPGJyPgo8Yj5zdHJpbmdyPC9iPjogbmVlZGVkIHRvIGNvbXB1dGUgc3RyaW5nIGxlbmd0aHMgPGJyPgoKV2UgYWxzbyBhZGQgPGk+ImhlbHBlcnMuUiI8L2k+IGFzIGEgZGVwZW5kZW5jeSwgd2hpY2ggaW5jbHVkZXMgY3VzdG9tIGNvZGUgZm9yIGltcGxlbWVudGluZyBlcnJvciBiYXJzIGluIGdncGxvdDIgdmlzdWFsaXphdGlvbnMuCgojIyBTZXQgR2xvYWJhbCBnZ3Bsb3QyIFRoZW1lCjxicj4KVGhpcyBqdXN0IG1ha2VzIGl0IHNvIHRoYXQgYWxsIGdncGxvdDIgcGxvdHMgd2lsbCBoYXZlIHRoZSBibGFjayAmIHdoaXRlIHRoZW1lOiAKCmBgYHtyfQp0aGVtZV9zZXQodGhlbWVfbWluaW1hbCgpKQpgYGAKCgojIERhdGEgSW1wb3J0aW5nICYgTWFuYWdlbWVudAoKUnNjcmlwdCBtZXJnZV9yZXN1bHRzLlIgZ2VuZGVyX3Byb2Nlc3NpbmdfYV9zZWxmcGFjZWRfcmVhZGluZ190aW1lX3N0dWR5X21lcmdlZC5jc3YgbWl4ZWQgZ2VuZGVyX3Byb2Nlc3NpbmdfZm9sbG93dXBfZGVtb2dyYXBoaWNzX21lcmdlZC5jc3YgcmVwdWJsaWNhbgoKCiMjIEltcG9ydCBEYXRhc2V0IAo8YnI+ClRoaXMgcmVhZHMgaW4gb3VyIC5jc3YgZmlsZSB3aXRoIGFsbCAzNyw4MDAgZGF0YSBwb2ludHMgbG9nZ2VkIGR1cmluZyB0aGUgZXhwZXJpbWVudC4gCgpgYGB7cn0KYWxsX2RhdGEgPC0gcmVhZC5jc3YoJ21lcmdlZF9hbGwuY3N2JykgJT4lCiAgc2VsZWN0KC1lcnJvcikKCmFsbF9kYXRhICU+JQogIGhlYWQoKQpgYGAKCgoKCiMjIERhdGEtUG9pbnQgRXhjbHVzaW9ucyB7LnRhYnNldH0KSG93ZXZlciwgd2UgZGVmaW5pdGVseSBkb24ndCB3YW50IGFsbCAzNyw4MDAgZGF0YSBwb2ludHMhIE1hbnkgb2YgdGhlc2UgYXJlbid0IGNyaXRpY2FsIHRyaWFscywgYW5kIHRoaXMgc3RpbGwgaW5jbHVkZXMgdGhlIGV4YW1wbGUgdHJpYWwgd2hlcmUgcGFydGljaXBhbnRzIHdlcmUgbGVhcm5pbmcgaG93IHRvIHRha2UgdGhlIGV4cGVyaW1lbnQuIEluIHRoaXMgc2VjdGlvbiB3ZSBmaWx0ZXIgb3V0OgoKLSBFeGFtcGxlIHRyaWFscwotIE5vbi1jcml0aWNhbCByZWdpb25zIG9mIHRoZSBzZW50ZW5jZXMKLSBQYXJ0aWNpcGFudHMgd2hvIGZhaWxlZCB0byByZWFjaCB0aGUgODUlIGFjY3VyYWN5IHRocmVzaG9sZCBvbiB0aGUgYXR0ZW50aW9uLWNoZWNrIHF1ZXN0aW9ucwoKT25jZSB3ZSBkbyB0aGlzLCB3ZSBhcmUgbGVmdCB3aXRoIGEgdG90YWwgb2YgMyw3MjAgZGF0YSBwb2ludHMhIDxicj4KClRoaXMgcGFydCBpcyBhIGJpdCB1Z2x5LCBzbyBJJ3ZlIGNvbGxhcHNlZCBlYWNoIG9mIHRoZXNlIGludG8gYSBzZXBhcmF0ZSB0YWIsIGJ1dCBhYnNvbHV0ZWx5IGZyZWUgdG8gZXhwbG9yZSBpZiB5b3UnZCBsaWtlISBUaGV5IGFyZSAoaG9wZWZ1bGx5KSB0aG9yb3VnaGx5IGFubm90YXRlZC4KCiMjIyBSZW1vdmluZyBFeGFtcGxlIFRyaWFscwo8YnI+IApUaGlzIHNpbXBseSBmaWx0ZXJzIG91dCBhbnkgZGF0YSBwb2ludCB3aGljaCBoYXMgdGhlIDxiPnRyaWFsX2lkPC9iPiA8aT4nZXhhbXBsZSc8L2k+LCB3aGljaCBpcyBhbGwgOCBkYXRhIHBvaW50cyBvZiB0aGUgZXhhbXBsZSB0cmlhbCwgZm9yIGVhY2ggb2YgdGhlIDIwMCBwYXJ0aWNpcGFudHMuIAo8YnI+CmBgYHtyfQphbGxfZGF0YSA8LSBhbGxfZGF0YSAlPiUgCiAgZmlsdGVyKHRyaWFsX2lkIT0gJ2V4YW1wbGUnKQoKYWxsX2RhdGEgJT4lIAogIHNlbGVjdCh3b3JrZXJpZCxydCx0cmlhbF9pZCkKYGBgCgojIyMgRmlsdGVyIG91dCBub24tY3JpdGljYWwgZGF0YSBwb2ludHMKPGJyPgpTaW1pbGFyIHRvIHRoZSBwcmV2aW91cyB0YWIgKFJlbW92aW5nIEV4YW1wbGUgVHJpYWxzKSwgdGhpcyBjb2RlIHJlbW92ZXMgZnJvbSBvdXIgZGF0YSBmcmFtZSBhbnkgbm9uLWNyaXRpY2FsIGRhdGEgcG9pbnRzLiBJbiB0aGlzIGNhc2UsIHdlIGFyZSBpbnRlcmVzdGVkIG9uIHRoZSByZWFkaW5nIHRpbWVzIG9uIHRoZSBwZXJzb25hbCBhbmQgcHJvZmVzc2lvbmFsIHRpdGxlcywgc28gdGhlc2UgaGF2ZSBiZWVuIGxvZ2dlZCBhcyB0aGUgPGk+J2NyaXRpY2FsJzwvaT4gdHJpYWxzIGluIHRoZSBleHBlcmltZW50LiBUaGlzIGNvZGUgZ2V0cyByaWQgb2YgZXZlcnl0aGluZyBlbHNlLgo8YnI+CmBgYHtyfQphbGxfZGF0YSA8LSBhbGxfZGF0YSAlPiUKICBmaWx0ZXIocmVnaW9uPT0nY3JpdGljYWwnKQoKYWxsX2RhdGEgJT4lIAogIHNlbGVjdCh3b3JrZXJpZCxydCxyZWdpb24pCmBgYAoKIyMjIFJ1bm5pbmcgRXhjbHVzaW9uIENyaXRlcmlhCjxicj4KVGhpcyBpcyB0aGUgbW9zdCBjb21wbGljYXRlZCBvZiB0aGUgcmVtb3ZhbCBzdGVwcywgc28gbGV0J3MgYnJlYWsgaXQgZG93bi4KCkZpcnN0LCB3ZSBuZWVkIHRvIGNyZWF0ZSBhIGRhdGEgZnJhbWUgd2l0aCB0aGUgPGI+d29ya2VyaWRzPC9iPiBvZiBldmVyeSBwYXJ0aWNpcGFudCB3aG8gZGlkIG5vdCBtZWV0IHRoZSA4NSUgYWNjdXJhY3kgdGhyZXNob2xkIG9uIHRoZSBhdHRlbnRpb24gY2hlY2tzLiBUaGUgYXR0ZW50aW9uIGNoZWNrcyAobG9nZ2VkIGFzIDxiPnJlc3BvbnNlX2NvcnJlY3Q8L2I+KSBoYXZlIGJpbmFyeSB2YWx1ZXM6IGEgPGk+MTwvaT4gbWVhbnMgdGhhdCB0aGV5IGdvdCB0aGUgcXVlc3Rpb24gY29ycmVjdCwgd2hpbGUgYSB6ZXJvICg8aT4wPC9pPikgaW5kaWNhdGVzIHRoYXQgdGhleSBkaWQgbm90Ljxicj4KPGJyPgpTbywgd2UgY3JlYXRlIGEgZGF0YSBmcmFtZSB3aXRoIHRoZSBwYXJ0aWNpcGFudHMgd2hvIGRpZG4ndCBtZWV0IHRoZSB0aHJlc2hvbGQgYnkgZ3JvdXBpbmcgYWxsIHRoZSBkYXRhIGJ5IHBhcnRpY2lwYW50LCBhbmQgdGhlbiBjcmVhdGluZyBhIHNtYWxsIGRhdGEgZnJhbWUgd2l0aCBqdXN0IHRoZSB3b3JrZXJzIGFuZCB0aGVpciByZWxhdGl2ZSBhY2N1cmFjaWVzLCB3aGljaCBpcyByZWNvcmRlZCBpbiB0aGUgbmV3IDxiPmFjY3VyYWN5PC9iPiBjb2x1bW4uIFRoaXMgaXMgdmFsdWVkIGJ5IG1lYW5pbmcgZWFjaCBwYXJ0aWNpcGFudCdzIDxiPnJlc3BvbnNlX2NvcnJlY3Q8L2I+IHNjb3JlczoKPGJyPgpgYGB7cn0KZXhjbHVzaW9uIDwtIGFsbF9kYXRhICU+JSBncm91cF9ieSh3b3JrZXJpZCxzeXN0ZW0uQnJvd3NlcikgJT4lCiAgc3VtbWFyaXNlKGFjY3VyYWN5ID0gbWVhbihyZXNwb25zZV9jb3JyZWN0KSkgCgpleGNsdXNpb24KYGBgCjxicj4KTm93IHdlIGNhbiBhZGQgdG8gdGhpcyBhbm90aGVyIGNvbHVtbiwgY2FsbGVkIDxiPmV4Y2x1ZGU8L2I+LCB3aGljaCB3aWxsIGFzc2lnbiB0aGVtIGEgdmFsdWUgb2YgPGk+J1llcyc8L2k+IGlmIHRoZWlyIGRhdGEgbmVlZHMgdG8gYmUgZXhjbHVkZWQgYmFzZWQgb24gdGhlaXIgYWNjdXJhY3kuIElmIHRoZXkgc2NvcmVkIG92ZXIgODUlIGFjY3VyYWN5LCB0aGVuIHRoZXkgZG8gbm90IG5lZWQgdG8gYmUgZXhjbHVkZWQsIHNvIHRoZWlyIDxiPmV4Y2x1ZGU8L2I+IHZhbHVlIGlzIDxpPidObyc8L2k+LgoKYGBge3J9CmV4Y2x1c2lvbiA8LSBleGNsdXNpb24gJT4lCiAgbXV0YXRlKGV4Y2x1ZGUgPSBpZmVsc2UoYWNjdXJhY3kgPCAwLjg1LCdZZXMnLCdObycpKQoKZXhjbHVzaW9uCmBgYAo8YnI+IApOb3cgd2Ugd2FudCB0byBqdXN0IGdldCBhIGxpc3Qgb2YgYWxsIHRoZSBwYXJ0aWNpcGFudHMgd2hvIGhhdmUgYmVlbiBhc3NpZ25lZCBhIDxpPidZZXMnPC9pPiB2YWx1ZSwgaW4gb3JkZXIgdG8gZXZlbnR1YWxseSByZW1vdmUgdGhlc2UgcGFydGljaXBhbnRzIGZyb20gYWxsIHRoZSBkYXRhLiBUaGlzIGNvZGUgZG9lcyB0aGF0LgoKYGBge3J9CmV4Y2x1c2lvbiA9IGV4Y2x1c2lvbiAlPiUgCiAgZmlsdGVyKGV4Y2x1ZGUgPT0gJ1llcycpICU+JQogIHNlbGVjdCh3b3JrZXJpZCxzeXN0ZW0uQnJvd3NlcikKCmV4Y2x1c2lvbgpgYGAKPGJyPgpOb3csIGZpbmFsbHksIHdlIGNhbiBleGNsdWRlIHRoZXNlIHBhcnRpY2lwYW50cyEgV2UgZG8gdGhpcyBieSByZWRlZmluaW5nIDxiPmFsbF9kYXRhPC9iPiBhcyBpdHNlbGYsIGJ1dCB3aXRob3V0IHRob3NlIHJvd3Mgd2hpY2ggaGF2ZSA8Yj53b3JrZXJpZDwvYj4gdmFsdWVzIGFwcGVhciBpbiB0aGUgbGlzdCB3ZSBqdXN0IG1hZGUgaW4gdGhlIGFib3ZlIHN0ZXAhIAoKYGBge3J9CmFsbF9kYXRhIDwtIGFsbF9kYXRhWyEoYWxsX2RhdGEkd29ya2VyaWQgJWluJSBleGNsdXNpb24kd29ya2VyaWQpLF0KCmFsbF9kYXRhICU+JSAKICBzZWxlY3Qod29ya2VyaWQscnQsdHJpYWxfaWQpCmBgYAo8YnI+CklmIHdlIHdhbnQgdG8gY2hlY2sgdGhhdCB0aGUgcmlnaHQgbnVtYmVyIHdhcyBzdWJ0cmFjdGVkLCB3ZSBjYW4gZ2V0IHRoZSBsZW5ndGggb2YgdGhlIGZpcnN0IGxpc3Qgd2UgbWFkZSAoPGI+ZXhjbHVzaW9uPC9iPikgYW5kIHNlZSBpZiB0aGF0IG1hdGNoZXMgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgbnVtYmVyIG9mIHVuaXF1ZSBwYXJ0aWNpcGFudHMgaW4gb3VyIG5ldyA8Yj5hbGxfZGF0YTwvYj4gZGF0YSBmcmFtZSBhbmQgdGhlIG9yaWdpbmFsIG9uZSAod2hpY2ggaGFkIDIwMCB0byBiZWdpbiB3aXRoKS4gVG8gZG8gdGhpcyB3ZSBnZXQgdGhlIGxlbmd0aCBvZiB0aGUgbGlzdCBvZiB1bmlxdWUgd29ya2VyIGlkcyBpbiB0aGUgPGI+YWxsX2RhdGE8L2I+IGRhdGEgZnJhbWUsIGFuZCBzdWJ0cmFjdCBpdCBmcm9tIDIwMC4gVGhlbiB3ZSBjb21wYXJlIHRoaXMgdG8gdGhlIGxlbmd0aCBvZiB0aGUgPGI+ZXhjbHVzaW9uPC9iPiBsaXN0LgoKYGBge3J9CjI5OCAtIGxlbmd0aCh1bmlxdWUoYWxsX2RhdGEkd29ya2VyaWQpKQoKbGVuZ3RoKHVuaXF1ZShleGNsdXNpb24kd29ya2VyaWQpKQpgYGAKPGJyPgpJZiB3ZSdyZSBiZWluZyByZWFsbHkgZXh0cmEsIHdlIGNhbiBhbHNvIHJ1biB0aGlzIGFzIGFuIGVxdWl2YWxlbmN5IHRvIGdldCBhIGJvb2xlYW4gVHJ1ZS9GYWxzZSB2YWx1ZToKCmBgYHtyfQoyOTggLSBsZW5ndGgodW5pcXVlKGFsbF9kYXRhJHdvcmtlcmlkKSkgPT0gbGVuZ3RoKHVuaXF1ZShleGNsdXNpb24kd29ya2VyaWQpKQpgYGAKCldlIGNhbiBhbHNvIGZpbHRlciBvdXQgYW55IHRyaWFscyB3aGljaCBmYWxsIHdpdGhvdXQgMi41IHN0YW5kYXJkIGRldmlhdGlvbnMgZm9yIHRoYXQgdHJpYWwuCgpgYGB7cn0KYWxsX2RhdGEgPC0gYWxsX2RhdGEgJT4lCiAgZ3JvdXBfYnkodHJpYWxfaWQpICU+JQogIG11dGF0ZShpZF9tZWFuID0gbWVhbihsb2cocnQpKSkgJT4lCiAgbXV0YXRlKGV4Y2x1c2lvbiA9IChsb2cocnQpIDwgbWVhbihsb2cocnQpKSAtIDIqc2QobG9nKHJ0KSl8KGxvZyhydCkgPiBtZWFuKGxvZyhydCkpICsgMipzZChsb2cocnQpKSkpKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgZmlsdGVyKGV4Y2x1c2lvbj09RkFMU0UpCmBgYAoKCiMjIEFkZGluZyBpbiBBZGRpdGlvbmFsIEltcG9ydGFudCBUcmlhbCBJbmZvcm1hdGlvbiB7LnRhYnNldH0KTm93IHRoYXQgd2UgaGF2ZSBvbmx5IHRoZSByb3dzIHdlIHdhbnQsIGxldCdzIGFkZCBzb21lIG5ldyBjb2x1bW5zLCB3aGljaCB3aWxsIGNvbnRhaW4gaW1wb3J0YW50IGluZm9ybWF0aW9uIGZvciBlYWNoIGRhdGEgcG9pbnQuIEhlcmUsIHdlIHdpbGwgYmUgYWRkaW5nOgoKLSBHZW5kZXIgSWRlb2xvZ3kgU3Vic2NvcmVzCi0gVHJpYWwgR2VuZGVycwotIFRyaWFsIE1vcnBob2xvZ3kgVHlwZXMKLSBDcml0aWNhbCBJdGVtIExlbmd0aAotIFRyaWFsIENvbmdydWVuY3kgCgpJZGVhbGx5LCBJIHdvdWxkJ3ZlIGFkZGVkIGFsbCBvZiB0aGVzZSBidXQgdGhlIGZpcnN0IHdoZW4gSSBhY3R1YWxseSBjcmVhdGVkIHRoZSBzdGltdWxpIGFuZCBsb2dnZWQgcmVzcG9uc2VzLCBidXQgSSBmb3Jnb3QgdG8hIEx1Y2tpbHksIFIgYWxsb3dzIHVzIHRvIGRvIHRoaXMgcG9zdC1ob2MgZmFpcmx5IHN0cmFpZ2h0Zm9yd2FyZGx5Li4uIHdoaWNoIGlzIGdvb2QsIHNpbmNlIHRoZXNlIGZlYXR1cmVzIHdpbGwgYmUgY3JpdGljYWwgaW4gb3VyIGRhdGEgdmlzdWFsaXphdGlvbiBhbmQgYW5hbHlzaXMuPGJyPgo8YnI+CkFnYWluLCBzb21lIG9mIHRoaXMgY29kZSBpcyBmYWlybHkgdWdseSBhbmQgaW52b2x2ZWQsIG9yIGlycmVsZXZhbnQsIHNvIEkndmUgb25jZSBhZ2FpbiBkaXZ2aWVkIGl0IHVwIGludG8gaW5kaXZpZHVhbCB0YWJzLCB3aGljaCB5b3UncmUgZnJlZSB0byBwZXJ1c2Ugb3Igbm90LgoKCiMjIyBEZWZpbmluZyBHZW5kZXIgU3Vic2NvcmVzCjxicj4KVGhlIHF1ZXN0aW9uIHVuZGVyIGludmVzdGlnYXRpb24gaGVyZSBpcyB3aGV0aGVyIG9yIG5vdCBpbmRpdmlkdWFscycgY29uY2VwdGlvbnMgb2YgZ2VuZGVyIGFmZmVjdCBob3cgdGhleSBwcm9jZXNzICBnZW5kZXJlZCBhbmQgZ2VuZGVyLW5ldXRyYWwgZm9ybXMgb2YgRW5nbGlzaCBwZXJzb25hbCBhbmQgcHJvZmVzc2lvbmFsIHRpdGxlcy4gPGJyPgo8YnI+CkluIG9yZGVyIHRvIGV4YW1pbmUgdGhpcywgd2UgbmVlZCB0byBxdWFuaWZ5IHBhcnRpY2lwYW50cycgaWRlb2xvZ2ljYWwgdmlld3MhIEhlcmUgd2UgaGF2ZSBhZG9wdGVkIHRoZSAxMy1pdGVtIFNvY2lhbCBSb2xlcyBRdWVzdGlvbm5haXJlIHB1dCBmb3J0aCBpbiBCYWJlciAmIFR1Y2tlciAoMjAwNikuIFF1ZXN0aW9ucyAxLTUgY29ycmVsYXRlIHRvIHRoZSA8aT4nR2VuZGVyIFRyYW5zY2VuZGVudCc8L2k+IHN1YnNjYWxlLCBhbmQgcXVlc3Rpb25zIDYtMTMgY29ycmVzcG9uZCB0byB0aGUgPGk+J0dlbmRlciBMaW5rZWQnPC9pPiBzdWJzY2FsZS4gRWFjaCBpdGVtIGlzIHNjb3JlZCBvbiBhIHNjYWxlIG9mIDAtMTAwLiBTbywgdGhlIGZpcnN0IHRoaW5nIHdlIHdhbnQgdG8gZG8gaXMgbWFrZSB0d28gbGlzdHMgb2YgY29sdW1ucyB3aGljaCBjb3JyZXNwb25kIHRvIHRoZXNlIHR3byBzdWJzY2FsZXMsIHNpbmNlIHRoZSBxdWVzdGlvbnMgYXJlIHN0b3JlZCBpbmRpdmlkdWFsbHkgaW4gdGhlIGRhdGE6CgpgYGB7cn0KZ2VuZGVyX3RyYW5zY2VuZGVuY2VfY29scyA8LSBjKCdzdWJqZWN0X2luZm9ybWF0aW9uLmdlbmRlcl9xMScsJ3N1YmplY3RfaW5mb3JtYXRpb24uZ2VuZGVyX3EyJywnc3ViamVjdF9pbmZvcm1hdGlvbi5nZW5kZXJfcTMnLCdzdWJqZWN0X2luZm9ybWF0aW9uLmdlbmRlcl9xNCcsJ3N1YmplY3RfaW5mb3JtYXRpb24uZ2VuZGVyX3E1JykKCmdlbmRlcl9saW5rZWRfY29scyA8LSBjKCdzdWJqZWN0X2luZm9ybWF0aW9uLmdlbmRlcl9xNicsJ3N1YmplY3RfaW5mb3JtYXRpb24uZ2VuZGVyX3E3Jywnc3ViamVjdF9pbmZvcm1hdGlvbi5nZW5kZXJfcTgnLCdzdWJqZWN0X2luZm9ybWF0aW9uLmdlbmRlcl9xOScsJ3N1YmplY3RfaW5mb3JtYXRpb24uZ2VuZGVyX3ExMCcsJ3N1YmplY3RfaW5mb3JtYXRpb24uZ2VuZGVyX3ExMScsJ3N1YmplY3RfaW5mb3JtYXRpb24uZ2VuZGVyX3ExMicsJ3N1YmplY3RfaW5mb3JtYXRpb24uZ2VuZGVyX3ExMycpCmBgYAo8YnI+Ck5vdyB3ZSBjYW4gdXNlIHRoZSBtdXRhdGUoKSBtZXRob2Qgb24gPGI+YWxsX2RhdGE8L2I+IHRvIGFkZCB0d28gbmV3IGNvbHVtbnMsIG9uZSBmb3IgZWFjaCBzdWJzY2FsZS4gV2UgdGVsbCBSIHRvIHRha2UgdGhlIG1lYW5zIG9mIHRoZSBzcGVjaWZpZWQgY29sdW1ucyBpbiBbY29sdW1uX25hbWVzXSBvZiA8Yj5hbGxfZGF0YTwvYj4gZm9yIGVhY2ggaW5kaXZpZHVhbCByb3c6IHJvd01lYW5zKGFsbF9kYXRhW2NvbHVtbl9uYW1lc10pLjxicj4KPGJyPgpXZSBhbHNvIGhhdmUgdG8gc3VidHJhY3QgdGhpcyBtZWFuIGZyb20gMTAwIGluIHRoZSBjYXNlIG9mIHRoZSA8aT4nR2VuZGVyIFRyYW5zY2VuZGVudCc8L2k+IHN1YnNjYWxlLCBzaW5jZSBpdCBpcyBpbnZlcnNlbHkgc2NvcmVkLiBUaGlzIGlzIGVhc3kgZW5vdWdoIHRvIGRvIGR1cmluZyB0aGUgbXV0YXRpb24gc3RlcDoKCmBgYHtyfQphbGxfZGF0YSA8LSBhbGxfZGF0YSAlPiUKICBtdXRhdGUoZ2VuZGVyX3RyYW5zID0gMTAwIC0gKHJvd01lYW5zKGFsbF9kYXRhW2dlbmRlcl90cmFuc2NlbmRlbmNlX2NvbHNdKSkpICU+JQogIG11dGF0ZShnZW5kZXJfbGluayA9IHJvd01lYW5zKGFsbF9kYXRhW2dlbmRlcl9saW5rZWRfY29sc10pKQoKYWxsX2RhdGEgJT4lCiAgc2VsZWN0KHdvcmtlcmlkLHJ0LGdlbmRlcl90cmFucyxnZW5kZXJfbGluaykgCmBgYAo8YnI+CkZpbmFsbHksIHdlIHByb2JhYmx5IHdhbnQgc29tZXRoaW5nIHRoYXQgaW5jbHVkZXMgdGhlIGF2ZXJhZ2UgYWNyb3NzIGFsbCB0aGUgZ2VuZGVyIHF1ZXN0aW9ucywgcmVnYXJkbGVzcyBvZiBzdWJzY29yZXMuIFRoaXMgaXMgZWFzeSBlbm91Z2gsIHNpbmNlIHdlIGp1c3QgaGF2ZSB0byBtZWFuIHRoZSB0d28gc3Vic2NvcmVzIHdlIGFscmVhZHkgbWFkZS4gU28sIGxldCdzIGRlZmluZSBhIGNvbHVtbiBsaXN0Ogo8YnI+CmBgYHtyfQpnZW5kZXJfYWxsID0gYygnZ2VuZGVyX3RyYW5zJywnZ2VuZGVyX2xpbmsnKQpgYGAKPGJyPgpOb3cgd2UgbXV0YXRlIGEgbmV3IGNvbHVtbiEgCjxicj4KYGBge3J9CmFsbF9kYXRhIDwtIGFsbF9kYXRhICU+JQogIG11dGF0ZShnZW5kZXJfdG90YWwgPSByb3dNZWFucyhhbGxfZGF0YVtnZW5kZXJfYWxsXSkpCgphbGxfZGF0YSAlPiUKICBzZWxlY3Qod29ya2VyaWQscnQsZ2VuZGVyX3RyYW5zLGdlbmRlcl9saW5rLGdlbmRlcl90b3RhbCkgCmBgYAoKCiMjIyBBZGRpbmcgR2VuZGVyCjxicj4KV2UgYWxzbyB3YW50IHRvIGFkZCB3aGV0aGVyIHRoZSB0cmlhbCBpbmNsdWRlZCBhIGZlbWFsZSBvciBtYWxlIHJlZmVyZW50IChidXQgYWxzbywgbGlrZSwgZGVzdHJveSB0aGUgYmluYXJ5ISkuIEluIG9yZGVyIHRvIGRvIHRoaXMsIHdlJ2xsIGp1c3QgYWRkIGEgPGI+dHJpYWxfZ2VuZGVyPC9iPiBjb2x1bW4gdGhhdCBzYXlzIDxpPidmZW1hbGUnPC9pPiBpZiB0aGUgY29uZGl0aW9uIHdhcyBlaXRoZXIgPGk+J25ldXRyYWxfZmVtYWxlJzwvaT4gb3IgPGk+J2NvbmdydWVudF9mZW1hbGUnPC9pPi4gT3RoZXJ3aXNlLCB3ZSB3YW50IHRoZSA8Yj50cmlhbF9nZW5kZXI8L2I+IHRvIHNheSA8aT4nbWFsZSc8L2k+Lgo8YnI+PGJyPgoKYGBge3J9CmFsbF9kYXRhIDwtIGFsbF9kYXRhICU+JQogIG11dGF0ZSh0cmlhbF9nZW5kZXIgPSBpZmVsc2UoY29uZGl0aW9uPT0nbmV1dHJhbF9mZW1hbGUnIHwgY29uZGl0aW9uID09ICdjb25ncnVlbnRfZmVtYWxlJywnZmVtYWxlJywnbWFsZScpKQoKYWxsX2RhdGEgJT4lCiAgc2VsZWN0KHdvcmtlcmlkLHJ0LGNvbmRpdGlvbix0cmlhbF9pZCx0cmlhbF9nZW5kZXIpCmBgYAoKIyMjIEFkZGluZyBNb3JwaG9sb2d5IFR5cGUKPGJyPgpOb3cgd2Ugd2FudCB0byBhZGQgd2hldGhlciBvciBub3QgdGhlIGxleGVtZSdzIG5ldXRyYWwgZm9ybSBpcyBkZXZlbG9wZWQgYnkgY29tcG91bmRpbmcgKGFzIGluIDxpPidjb25ncmVzcy1wZXJzb24nPC9pPikgb3IgYnkgdGhlIGFkb3B0aW9uIG9mIHRoZSBtYWxlIGZvcm0gKGFzIGluIDxpPidhY3Rvcic8L2k+IGJlaW5nIHVzZWQgbW9yZSBmb3IgYm90aCBtZW4gYW5kIHdvbWVuKS4gSW4gdGhpcyBzdHVkeSwgd2Ugb25seSBoYXZlIHNpeCBsZXhlbWVzIG9mIHRoZSBsYXR0ZXIgdHlwZSwgc28gd2UnbGwganVzdCB0ZWxsIFIgdG8gYXNzaWduIHRob3NlIGEgPGI+bW9ycGhfdHlwZTwvYj4gdmFsdWUgb2YgPGk+J2Fkb3B0aW9uJzwvaT4gKGZvciAnbWFsZSBhZG9wdGlvbicpLCBhbmQgYWxsIGVsc2Ugd2lsbCBiZSBhc3NpZ25lZCBhIHZhbHVlIG9mIDxpPidjb21wb3VuZCc8L2k+Lgo8YnI+PGJyPgoKYGBge3J9CmFsbF9kYXRhIDwtIGFsbF9kYXRhJT4lCiAgbXV0YXRlKG1vcnBoX3R5cGUgPSBpZmVsc2UobGV4ZW1lIT0gJ2FjdG9yJyAmIGxleGVtZSE9ICdob3N0JyAmIGxleGVtZSAhPSdodW50ZXInICYgbGV4ZW1lIT0gJ3ZpbGxhaW4nICYgbGV4ZW1lIT0gJ2hlaXInICYgbGV4ZW1lIT0gJ2hlcm8nLCdjb21wb3VuZCcsJ2Fkb3B0aW9uJykpCgphbGxfZGF0YSAlPiUKICBzZWxlY3QocnQsbGV4ZW1lLG1vcnBoX3R5cGUpCmBgYAoKCiMjIyBBZGRpbmcgRm9ybSBMZW5ndGgKPGJyPgpBbm90aGVyIGltcG9ydGFudCBmYWN0b3Igd2Ugd2FudCB0byBleHBsb3JlIGlzIHRoZSBsZW5ndGggb2YgdGhlIGNyaXRpY2FsIGl0ZW0hIEluIG9yZGVyIHRvIGFkZCB0aGlzLCB3ZSBzaW1wbHkgY3JlYXRlIGEgbmV3IGNvbHVtbiA8Yj5mb3JtX2xlbmd0aDwvYj4gYW5kIHRlbGwgUiB0byBpbnB1dCBhcyB0aGF0IGNvbHVtbidzIHZhbHVlIHRoZSBsZW5ndGggb2YgdGhlIHN0cmluZyB0aGF0IGFwcGVhcnMgaW4gdGhhdCByb3cncyA8Yj5mb3JtPC9iPiBjb2x1bW4sIHdoaWNoIGNvcnJlc3BvbmRzIHRvIHRoZSBvcnRob2dyYXBpYyBmb3JtIG9mIHRoZSBjcml0aWNhbCBpdGVtIGluIHRoYXQgdHJpYWwuIDxpPk5vdGUgdGhhdCB0aGlzIHdpbGwgaW5jbHVkZSBzcGFjZXMgaW4gdGhlIGNvdW50ITwvaT4KPGJyPjxicj4KCmBgYHtyfQphbGxfZGF0YSA8LSBhbGxfZGF0YSAlPiUKICBtdXRhdGUoZm9ybV9sZW5ndGggPSBzdHJfbGVuZ3RoKGZvcm0pKQoKYWxsX2RhdGEgJT4lCiAgc2VsZWN0KHJ0LGxleGVtZSxmb3JtLGZvcm1fbGVuZ3RoKQpgYGAKCgojIyMgQWRkaW5nIENvbmdydWVuY3kgQ29sdW1uCjxicj4KRmluYWxseSwgbGV0J3MgbWFrZSBzdXJlIHdlIGhhdmUgYSBjb2x1bW4gd2hpY2ggcmVjb3JkcyB3aGV0aGVyIG9yIG5vdCB0aGUgdHJpYWwgd2FzIGdlbmRlci1jb25ncnVlbnQgKGFzIGluIDxpPidTaGVsYnkgaXMgYSBjb25ncmVzc3dvbWFuJzwvaT4pIG9yIGdlbmRlciBuZXV0cmFsIChhcyBpbiA8aT4nU2hlbGJ5IGlzIGEgY29uZ3Jlc3NwZXJzb24nPC9pPikuIFdlIGFkZCBhIDxiPnRyaWFsX2NvbmdydWVuY3k8L2I+IGNvbHVtbiwgd2hpY2ggaXMgdmFsdWVkIGFzIDxpPidjb25ncnVlbnQnPC9pPiBpZiB0aGF0IHJvdydzIGNvbmRpdGlvbiBpcyBvbmUgb2YgdGhlIHR3byBjb25ncnVlbnQgY29uZGl0aW9ucy4gT3RoZXJ3aXNlLCBpdCBnZXRzIHZhbHVlZCBhcyA8aT4nbmV1dHJhbCc8L2k+LgoKYGBge3J9CmFsbF9kYXRhIDwtIGFsbF9kYXRhICU+JQogIG11dGF0ZSh0cmlhbF9jb25ncnVlbmN5ID0gaWZlbHNlKGNvbmRpdGlvbj09J2NvbmdydWVudF9tYWxlJyB8IGNvbmRpdGlvbiA9PSAnY29uZ3J1ZW50X2ZlbWFsZScsJ2NvbmdydWVudCcsJ25ldXRyYWwnKSkKCmFsbF9kYXRhICU+JQogIHNlbGVjdChydCxjb25kaXRpb24sdHJpYWxfY29uZ3J1ZW5jeSkKYGBgCgojIyMjIEFkZGluZyBSZWFkaW5nIFRpbWUgUmVzaWR1YWxzCgoKYGBge3J9CnNpbXBsZV9tb2RlbCA8LSBsbShsb2cocnQpfmZvcm1fbGVuZ3RoLCBkYXRhID0gYWxsX2RhdGEpCmBgYAoKYGBge3J9CnN1bW1hcnkoc2ltcGxlX21vZGVsKQpgYGAKCmBgYHtyfQphbGxfZGF0YSA8LSBhbGxfZGF0YSAlPiUKICBtdXRhdGUocmVzaWRfcnQgPSByZXNpZChzaW1wbGVfbW9kZWwpKQpgYGAKCmBgYHtyfQpnZ3Bsb3QoZGF0YT1hbGxfZGF0YSwgYWVzKHg9bG9nKHJ0KSx5PXJlc2lkX3J0KSkgKyAKICBnZW9tX3BvaW50KCkKYGBgCgojIyMgQWRkaW5nIExhcmdlIFBvbGl0aWNhbCBNYWNyb2NhdGVnb3JpZXMKCmBgYHtyfQphbGxfZGF0YSA8LSBhbGxfZGF0YSAlPiUKICBtdXRhdGUocG9saV9wYXJ0eSA9IGlmZWxzZShzdWJqZWN0X2luZm9ybWF0aW9uLnBhcnR5X2FsaWdubWVudCA9PSAxIHwgc3ViamVjdF9pbmZvcm1hdGlvbi5wYXJ0eV9hbGlnbm1lbnQgPT0gMiwnUmVwdWJsaWNhbicsaWZlbHNlKHN1YmplY3RfaW5mb3JtYXRpb24ucGFydHlfYWxpZ25tZW50ID09IDQgfCBzdWJqZWN0X2luZm9ybWF0aW9uLnBhcnR5X2FsaWdubWVudCA9PSA1LCdEZW1vY3JhdCcsJ05vbi1QYXJ0aXNhbicpKSkKYGBgCgoKIyMgUmV2aWV3IHN1bW1hcnkgd2l0aCBuZXcgY29sdW1ucyAKPGJyPgpOb3cgd2UgY2FuIHVzZSB0aGUgaGVhZCgpIG1ldGhvZCB0byBjaGVjayB0aGUgY3VycmVudCBzdGF0ZSBvZiB0aGUgZGF0YSBmcmFtZSwgd2hpY2ggc2hvdWxkIGluY2x1ZGUgYSBncmFuZCB0b3RhbCBvZiA0NiBjb2x1bW5zISAKPGJyPgoKYGBge3J9CmhlYWQoYWxsX2RhdGEpCmBgYAo8YnI+CkFuZCBub3csIG91ciBkYXRhIHNob3VsZCBiZSBjb21wbGV0ZWx5IHJlYWR5IHRvIHZpc3VhbGl6ZSBhbmQgYW5hbHl6ZSEgCgojIERhdGEgVmlzdWFsaXphdGlvbgoKPGI+W1RISVMgUEFSVCBVTkRFUiBDT05TVFJVQ1RJT046IENPTUlORyBTT09OXTwvYj4KCk5vdyB0aGF0IHdlIGhhdmUgb3VyIGRhdGEgcmVhZHkgZm9yIHZpc3VhbGl6YXRpb24gYW5kIGFuYWx5c2lzLCBsZXQncyBkbyB0aGUgZm9ybWVyISAKPGJyPgoKIyMgQSBQcmVsaW1pbmFyeSAKCmBgYHtyfQppbmF1Z3VyYXRpb25fMjAyMSA9IGMoIiM1NDQ1YjEiLCAiIzc0OWRhZSIsICIjZjNjNDgzIiwgIiM1YzFhMzMiLCAiI2NkMzM0MSIsIiNmN2RjNmEiKQpgYGAKCmBgYHtyfQpjYlBhbGV0dGUgPC0gYygiIzk5OTk5OSIsICIjRTY5RjAwIiwgIiM1NkI0RTkiLCAiIzAwOUU3MyIsICIjRjBFNDQyIiwgIiMwMDcyQjIiLCAiI0Q1NUUwMCIsICIjQ0M3OUE3IikKYGBgCgoKIyMgUGxvdHMgey50YWJzZXR9CgojIyMgTG9nIFJlYWRpbmcsIG5vbi1yZXNpZHVhbAogCmBgYHtyfQpnZ3Bsb3QoYWxsX2RhdGEsIGFlcyh4PXJ0LCBmaWxsPW1vcnBoX3R5cGUpKSArIAogIGdlb21fZGVuc2l0eShhbHBoYT0uNikgKyAKICBsYWJzKHg9IlJhdyBSZWFkaW5nIFRpbWUiLCB5PSJEZW5zaXR5IixmaWxsPSJDcml0aWNhbCBJdGVtIE1vcnBob2xvZ3kgVHlwZSIpICsgCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY2JQYWxldHRlKQpgYGAKCmBgYHtyfQogZ2dzYXZlKCJwbG90cy9tb3JwaF90eXBlX2RlbnNpdHkuanBnIixoZWlnaHQ9NSx3aWR0aD04KQpgYGAKCgpgYGB7cn0KZ2dwbG90KGFsbF9kYXRhLCBhZXMoeD1ydCwgZmlsbD1jb25kaXRpb24pKSArIAogIGdlb21fZGVuc2l0eShhbHBoYT0uNCkgKyAKICBsYWJzKHg9IlJhdyBSZWFkaW5nIFRpbWUiLCB5PSJEZW5zaXR5IixmaWxsPSJDcml0aWNhbCBJdGVtIENvbmRpdGlvbiIpICsgCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY2JQYWxldHRlKQpgYGAKCmBgYHtyfQpnZ3Bsb3QoYWxsX2RhdGEsIGFlcyh4PXJ0LCBmaWxsPXRyaWFsX2dlbmRlcikpICsgCiAgZ2VvbV9kZW5zaXR5KGFscGhhPS42KSArIAogIGxhYnMoeD0iUmF3IFJlYWRpbmcgVGltZSIsIHk9IkRlbnNpdHkiLGZpbGw9IkNyaXRpY2FsIEl0ZW0gR2VuZGVyIikgKyAKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjYlBhbGV0dGUpCmBgYAoKYGBge3J9CmdncGxvdChhbGxfZGF0YSwgYWVzKHg9cnQsIGZpbGw9dHJpYWxfY29uZ3J1ZW5jeSkpICsgCiAgZ2VvbV9kZW5zaXR5KGFscGhhPS42KSArIAogIGxhYnMoeD0iUmF3IFJlYWRpbmcgVGltZSIsIHk9IkRlbnNpdHkiLGZpbGw9IkNyaXRpY2FsIEl0ZW0gQ29uZ3J1ZW5jeSIpICsgCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY2JQYWxldHRlKQpgYGAKCmBgYHtyfQpnZ3Bsb3QoYWxsX2RhdGEsIGFlcyh4PWdlbmRlcl90b3RhbCwgeT1ydCkpICsgCiAgZ2VvbV9wb2ludChhbHBoYT0uNSkgKyAKICBnZW9tX3Ntb290aChtZXRob2QgPSAnbG0nLCBzaXplPTEuMikKYGBgCgpgYGB7cn0KYWxsX2RhdGEgJT4lCiAgZ3JvdXBfYnkoc3ViamVjdF9pbmZvcm1hdGlvbi5wYXJ0eV9hbGlnbm1lbnQsY29uZGl0aW9uKSAlPiUKICBzdW1tYXJpemUoTWVhblJUID0gbWVhbihydCksIENJLkxvdyA9IGNpLmxvdyhydCksIENJLkhpZ2ggPSBjaS5oaWdoKHJ0KSkgJT4lCiAgbXV0YXRlKFlNaW4gPSBNZWFuUlQgLSBDSS5Mb3csIFlNYXggPSBNZWFuUlQgKyBDSS5IaWdoKSAlPiUKICBnZ3Bsb3QoYWVzKHg9c3ViamVjdF9pbmZvcm1hdGlvbi5wYXJ0eV9hbGlnbm1lbnQseT1NZWFuUlQsZmlsbD1zdWJqZWN0X2luZm9ybWF0aW9uLnBhcnR5X2FsaWdubWVudCkpICsgCiAgZ2VvbV9iYXIoc3RhdD0naWRlbnRpdHknKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKyAKICBnZW9tX2Vycm9yYmFyKGFlcyh5bWluPVlNaW4seW1heD1ZTWF4KSx3aWR0aD0uMjUpICsgCiAgZmFjZXRfd3JhcCh+IGNvbmRpdGlvbikKYGBgCgoKCmBgYHtyfQpnZ3Bsb3QoYWxsX2RhdGEsIGFlcyh4PWZvcm1fbGVuZ3RoLHk9cnQpKSArIAogIGdlb21fcG9pbnQoKQpgYGAKCgoKYGBge3J9CmFnciA8LSBhbGxfZGF0YSAlPiUKICBncm91cF9ieSh0cmlhbF9nZW5kZXIsdHJpYWxfY29uZ3J1ZW5jeSkgJT4lCiAgc3VtbWFyaXplKE1lYW5SVCA9IG1lYW4ocnQpLCBDSS5Mb3cgPSBjaS5sb3cocnQpLCBDSS5IaWdoID0gY2kuaGlnaChydCkpICU+JQogIG11dGF0ZShZTWluID0gTWVhblJUIC0gQ0kuTG93LCBZTWF4ID0gTWVhblJUICsgQ0kuSGlnaCkKCmRvZGdlID0gcG9zaXRpb25fZG9kZ2UoLjkpCmdncGxvdChkYXRhPWFnciwgYWVzKHg9dHJpYWxfZ2VuZGVyLHk9TWVhblJULGZpbGw9dHJpYWxfY29uZ3J1ZW5jeSkpICsgCiAgZ2VvbV9iYXIoc3RhdD0naWRlbnRpdHknLHBvc2l0aW9uPWRvZGdlKSArIAogIGdlb21fZXJyb3JiYXIoYWVzKHltaW49WU1pbix5bWF4PVlNYXgpLHdpZHRoPS4yNSxwb3NpdGlvbj1kb2RnZSkKYGBgCgpgYGB7cn0KdGVtcCA8LSBhbGxfZGF0YSAlPiUKICBncm91cF9ieSh0cmlhbF9nZW5kZXIpICU+JQogIHN1bW1hcml6ZShNZWFuUlQgPSBtZWFuKHJ0KSwgQ0kuTG93ID0gY2kubG93KHJ0KSwgQ0kuSGlnaCA9IGNpLmhpZ2gocnQpKSAlPiUKICBtdXRhdGUoWU1pbiA9IE1lYW5SVCAtIENJLkxvdywgWU1heCA9IE1lYW5SVCArIENJLkhpZ2gpCgpkb2RnZSA9IHBvc2l0aW9uX2RvZGdlKC45KQpnZ3Bsb3QoZGF0YT10ZW1wLCBhZXMoeD10cmlhbF9nZW5kZXIseT1NZWFuUlQsZmlsbD10cmlhbF9nZW5kZXIpKSArIAogIGdlb21fYmFyKHN0YXQ9J2lkZW50aXR5Jyxwb3NpdGlvbj1kb2RnZSkgKyAKICBnZW9tX2Vycm9yYmFyKGFlcyh5bWluPVlNaW4seW1heD1ZTWF4KSx3aWR0aD0uMjUscG9zaXRpb249ZG9kZ2UpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAnbm9uZScpCmBgYAoKCmBgYHtyfQphbGxfZGF0YSAlPiUKZ2dwbG90KGFlcyh4PW1vcnBoX3R5cGUsIHk9cnQpKSArIAogIGdlb21fYm94cGxvdCgpCmBgYApgYGB7cn0KYWdnX3NwZWFrZXJfbWVhbl9sZW5ndGggPC0gYWxsX2RhdGEgJT4lCiAgZ3JvdXBfYnkoZm9ybV9sZW5ndGgsd29ya2VyaWQpICU+JQogIHN1bW1hcml6ZShNZWFuUlQ9bWVhbihydCkpCmBgYAoKCgpgYGB7cn0KYWxsX2RhdGEgJT4lCiAgZ3JvdXBfYnkoZm9ybV9sZW5ndGgpICU+JQogIHN1bW1hcml6ZShNZWFuUlQgPSBtZWFuKHJ0KSwgQ0kuTG93ID0gY2kubG93KHJ0KSwgQ0kuSGlnaCA9IGNpLmhpZ2gocnQpKSAlPiUKICBtdXRhdGUoWU1pbiA9IE1lYW5SVCAtIENJLkxvdywgWU1heCA9IE1lYW5SVCArIENJLkhpZ2gpICU+JQogIGdncGxvdChhZXMoeD1mb3JtX2xlbmd0aCx5PU1lYW5SVCkpICsgCiAgZ2VvbV9jb2woKSArIAogIGdlb21faml0dGVyKGRhdGEgPSBhZ2dfc3BlYWtlcl9tZWFuX2xlbmd0aCwgYWVzKHk9TWVhblJUKSxhbHBoYT0uMixjb2xvcj0nYmx1ZScpICsgCiAgZ2VvbV9lcnJvcmJhcihhZXMoeW1pbj1ZTWluLHltYXg9WU1heCksIHdpZHRoPS4yNSkgKyAKICBnZW9tX3Ntb290aChtZXRob2QgPSAnbG0nLCBzaXplPTEuMikgCmBgYAoKYGBge3J9CmFsbF9kYXRhICU+JQogIGdncGxvdChhZXMoeD1mb3JtX2xlbmd0aCwgeT1ydCxjb2xvcj1tb3JwaF90eXBlKSkgKyAKICBnZW9tX2ppdHRlcigpICsgCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gJ2xtJywgc2l6ZT0xLjIpIApgYGAKCmBgYHtyfQphbGxfZGF0YSAlPiUKICBnZ3Bsb3QoYWVzKHg9bG9nKGdlbmRlcl90cmFucyksIHk9cmVzaWRfcnQsY29sb3I9bW9ycGhfdHlwZSkpICsgCiAgZ2VvbV9qaXR0ZXIoKSArIAogIGdlb21fc21vb3RoKG1ldGhvZCA9ICdsbScsIHNpemU9MS4yKSAKYGBgCgpgYGB7cn0KYWxsX2RhdGEgJT4lCiAgZ2dwbG90KGFlcyh4PXN1YmplY3RfaW5mb3JtYXRpb24uYWdlLCB5PXJlc2lkX3J0LGNvbG9yPW1vcnBoX3R5cGUpKSArIAogIGdlb21faml0dGVyKCkgKyAKICBnZW9tX3Ntb290aChtZXRob2QgPSAnbG0nLCBzaXplPTEuMikgCmBgYAoKYGBge3J9CmFsbF9kYXRhICU+JQogIGZpbHRlcighaXMubmEocG9saV9wYXJ0eSkpICU+JQogIGdyb3VwX2J5KHdvcmtlcmlkLHN1YmplY3RfaW5mb3JtYXRpb24uZWR1Y2F0aW9uKSAlPiUKICBnZ3Bsb3QoYWVzKHg9c3ViamVjdF9pbmZvcm1hdGlvbi5lZHVjYXRpb24pKSArIAogIGdlb21fYmFyKCkgKyAKICBmYWNldF93cmFwKH5wb2xpX3BhcnR5KQpgYGAKCgojIyMgUmVzaWR1YWwgQW5hbHlzZXMKCmBgYHtyfQphZ2dfc3BlYWtlcl9tZWFuIDwtIGFsbF9kYXRhICU+JQogIGdyb3VwX2J5KG1vcnBoX3R5cGUsd29ya2VyaWQpICU+JQogIHN1bW1hcml6ZShNZWFuUlQ9bWVhbihyZXNpZF9ydCkpCmBgYAoKCmBgYHtyfQphbGxfZGF0YSAlPiUKICBncm91cF9ieShtb3JwaF90eXBlKSAlPiUKICBzdW1tYXJpemUoTWVhblJUID0gbWVhbihyZXNpZF9ydCksIENJLkxvdyA9IGNpLmxvdyhyZXNpZF9ydCksIENJLkhpZ2ggPSBjaS5oaWdoKHJlc2lkX3J0KSkgJT4lCiAgbXV0YXRlKFlNaW4gPSBNZWFuUlQgLSBDSS5Mb3csIFlNYXggPSBNZWFuUlQgKyBDSS5IaWdoKSAlPiUKICBnZ3Bsb3QoYWVzKHg9bW9ycGhfdHlwZSx5PU1lYW5SVCkpICsgCiAgZ2VvbV9wb2ludChzaXplPTMpICsgCiAgZ2VvbV9qaXR0ZXIoZGF0YSA9IGFnZ19zcGVha2VyX21lYW4sIGFlcyh5PU1lYW5SVCksYWxwaGE9LjIsY29sb3I9J2JsdWUnKSArIAogIGdlb21fZXJyb3JiYXIoYWVzKHltaW49WU1pbix5bWF4PVlNYXgpLCB3aWR0aD0uMjUpCmBgYAoKCmBgYHtyfQphZ2dfc3BlYWtlcl9tZWFuX2NvbiA8LSBhbGxfZGF0YSAlPiUKICBncm91cF9ieShjb25kaXRpb24sd29ya2VyaWQpICU+JQogIHN1bW1hcml6ZShNZWFuUlQ9bWVhbihyZXNpZF9ydCkpCmBgYAoKYGBge3J9CmFsbF9kYXRhICU+JQogIGdyb3VwX2J5KGNvbmRpdGlvbix0cmlhbF9nZW5kZXIpICU+JQogIHN1bW1hcml6ZShNZWFuUlQgPSBtZWFuKHJlc2lkX3J0KSwgQ0kuTG93ID0gY2kubG93KHJlc2lkX3J0KSwgQ0kuSGlnaCA9IGNpLmhpZ2gocmVzaWRfcnQpKSAlPiUKICBtdXRhdGUoWU1pbiA9IE1lYW5SVCAtIENJLkxvdywgWU1heCA9IE1lYW5SVCArIENJLkhpZ2gpICU+JQogIGdncGxvdChhZXMoeD1jb25kaXRpb24seT1NZWFuUlQsY29sb3I9dHJpYWxfZ2VuZGVyKSkgKyAKICBnZW9tX3BvaW50KHNpemU9MykgKyAKICBnZW9tX2ppdHRlcihkYXRhID0gYWdnX3NwZWFrZXJfbWVhbl9jb24sIGFlcyh5PU1lYW5SVCksYWxwaGE9LjIsY29sb3I9J21lZGl1bXNsYXRlYmx1ZScpICsgCiAgZ2VvbV9lcnJvcmJhcihhZXMoeW1pbj1ZTWluLHltYXg9WU1heCksIHdpZHRoPS4yNSkgKyAKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gY2JQYWxldHRlKQpgYGAKCmBgYHtyfQphbGxfZGF0YSAlPiUKICBncm91cF9ieShjb25kaXRpb24sdHJpYWxfZ2VuZGVyLHRyaWFsX2NvbmdydWVuY3ksbGV4ZW1lKSAlPiUKICBzdW1tYXJpemUoTWVhblJUID0gbWVhbihyZXNpZF9ydCksIENJLkxvdyA9IGNpLmxvdyhyZXNpZF9ydCksIENJLkhpZ2ggPSBjaS5oaWdoKHJlc2lkX3J0KSkgJT4lCiAgbXV0YXRlKFlNaW4gPSBNZWFuUlQgLSBDSS5Mb3csIFlNYXggPSBNZWFuUlQgKyBDSS5IaWdoKSAlPiUKICBnZ3Bsb3QoYWVzKHg9Y29uZGl0aW9uLHk9TWVhblJULGNvbG9yPXRyaWFsX2dlbmRlcixzaGFwZT10cmlhbF9jb25ncnVlbmN5KSkgKyAKICBnZW9tX3BvaW50KHNpemU9MykgKwogIGdlb21fZXJyb3JiYXIoYWVzKHltaW49WU1pbix5bWF4PVlNYXgpLCB3aWR0aD0uMjUpICsgCiAgZmFjZXRfd3JhcCh+IGxleGVtZSkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIHZqdXN0ID0gLjcsIGhqdXN0PS43KSkgKyAKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gY2JQYWxldHRlKQpgYGAKCmBgYHtyfQp0ZW1wIDwtIGFsbF9kYXRhICU+JQogIGdyb3VwX2J5KGxleGVtZSx0cmlhbF9nZW5kZXIsdHJpYWxfY29uZ3J1ZW5jeSkgJT4lCiAgc3VtbWFyaXplKG1lYW5SVCA9IG1lYW4ocmVzaWRfcnQpKSAlPiUKICBzcHJlYWQodHJpYWxfY29uZ3J1ZW5jeSxtZWFuUlQpICU+JQogIG11dGF0ZShjb25fZGlmID0gbmV1dHJhbC1jb25ncnVlbnQpCgpnZ3Bsb3QodGVtcCwgYWVzKHg9dHJpYWxfZ2VuZGVyLHk9Y29uX2RpZiwgZmlsbD10cmlhbF9nZW5kZXIpKSArCiAgZ2VvbV9iYXIoc3RhdD0naWRlbnRpdHknKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICdub25lJykgKyAKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPTApICsgCiAgZmFjZXRfd3JhcCh+bGV4ZW1lKSArIAogIGxhYnMoeCA9ICdUcmlhbCBHZW5kZXInLCB5PSdDb25ncnVlbmN5IERpZmZlcmVuY2UgKE5ldXRyYWwtQ29uZ3J1ZW50KScpCmBgYAoKYGBge3J9CmFsbF9kYXRhICU+JQogIGZpbHRlcihsZXhlbWU9PSdmaXJlZmlnaHRlcicpICU+JQogIGdyb3VwX2J5KHRyaWFsX2dlbmRlcikgJT4lCiAgc3VtbWFyaXplKE1lYW5SVCA9IG1lYW4ocmVzaWRfcnQpLCBDSS5Mb3cgPSBjaS5sb3cocmVzaWRfcnQpLCBDSS5IaWdoID0gY2kuaGlnaChyZXNpZF9ydCkpICU+JQogIG11dGF0ZShZTWluID0gTWVhblJUIC0gQ0kuTG93LCBZTWF4ID0gTWVhblJUICsgQ0kuSGlnaCkgJT4lCiAgZ2dwbG90KGFlcyh4PXRyaWFsX2dlbmRlcix5PU1lYW5SVCxjb2xvcj10cmlhbF9nZW5kZXIpKSArIAogIGdlb21fcG9pbnQoc2l6ZT0zKSArCiAgZ2VvbV9lcnJvcmJhcihhZXMoeW1pbj1ZTWluLHltYXg9WU1heCksIHdpZHRoPS4yNSkKYGBgCgoKCmBgYHtyfQpnZ3Bsb3QoYWxsX2RhdGEsIGFlcyh4PXRyaWFsX2dlbmRlcix5PXJlc2lkX3J0KSkgKyAKICBnZW9tX2JhcihzdGF0PSdpZGVudGl0eScpICsgCiAgZmFjZXRfd3JhcCh+bGV4ZW1lKQpgYGAKCgpgYGB7cn0KIGdnc2F2ZSgicGxvdHMvZGlmZmVyZW5jZS1wbG90LmpwZyIsaGVpZ2h0PTUsd2lkdGg9OCkKYGBgCgoKYGBge3J9CmFsbF9kYXRhICU+JQogIGZpbHRlcighaXMubmEocG9saV9wYXJ0eSkpICU+JQogIGdyb3VwX2J5KHBvbGlfcGFydHksY29uZGl0aW9uLHRyaWFsX2dlbmRlcix0cmlhbF9jb25ncnVlbmN5KSAlPiUKICBzdW1tYXJpemUoTWVhblJUID0gbWVhbihyZXNpZF9ydCksIENJLkxvdyA9IGNpLmxvdyhyZXNpZF9ydCksIENJLkhpZ2ggPSBjaS5oaWdoKHJlc2lkX3J0KSkgJT4lCiAgbXV0YXRlKFlNaW4gPSBNZWFuUlQgLSBDSS5Mb3csIFlNYXggPSBNZWFuUlQgKyBDSS5IaWdoKSAlPiUKICBnZ3Bsb3QoYWVzKHg9Y29uZGl0aW9uLHk9TWVhblJULGNvbG9yPXRyaWFsX2dlbmRlcixzaGFwZT10cmlhbF9jb25ncnVlbmN5KSkgKyAKICBnZW9tX3BvaW50KHNpemU9MykgKwogIGdlb21fZXJyb3JiYXIoYWVzKHltaW49WU1pbix5bWF4PVlNYXgpLCB3aWR0aD0uMjUpICsgCiAgZmFjZXRfd3JhcCh+IHBvbGlfcGFydHksIG5yb3cgPSAxKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgdmp1c3QgPSAuNywgaGp1c3Q9LjcpKSArIAogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjYlBhbGV0dGUpCmBgYAoKYGBge3J9CmFsbF9kYXRhICU+JQogIGZpbHRlcighaXMubmEoc3ViamVjdF9pbmZvcm1hdGlvbi5wYXJ0eV9hbGlnbm1lbnQpKSAlPiUKICBncm91cF9ieShzdWJqZWN0X2luZm9ybWF0aW9uLnBhcnR5X2FsaWdubWVudCxjb25kaXRpb24sdHJpYWxfZ2VuZGVyLHRyaWFsX2NvbmdydWVuY3kpICU+JQogIHN1bW1hcml6ZShNZWFuUlQgPSBtZWFuKHJlc2lkX3J0KSwgQ0kuTG93ID0gY2kubG93KHJlc2lkX3J0KSwgQ0kuSGlnaCA9IGNpLmhpZ2gocmVzaWRfcnQpKSAlPiUKICBtdXRhdGUoWU1pbiA9IE1lYW5SVCAtIENJLkxvdywgWU1heCA9IE1lYW5SVCArIENJLkhpZ2gpICU+JQogIGdncGxvdChhZXMoeD1jb25kaXRpb24seT1NZWFuUlQsY29sb3I9dHJpYWxfZ2VuZGVyLHNoYXBlPXRyaWFsX2NvbmdydWVuY3kpKSArIAogIGdlb21fcG9pbnQoc2l6ZT0zKSArCiAgZ2VvbV9lcnJvcmJhcihhZXMoeW1pbj1ZTWluLHltYXg9WU1heCksIHdpZHRoPS4yNSkgKyAKICBmYWNldF93cmFwKH4gc3ViamVjdF9pbmZvcm1hdGlvbi5wYXJ0eV9hbGlnbm1lbnQsIG5yb3cgPSAxKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgdmp1c3QgPSAuNywgaGp1c3Q9LjcpKSArIAogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjYlBhbGV0dGUpCmBgYAoKYGBge3J9CmFnZ3Jfc3BlYWtlciA8LSBhbGxfZGF0YSAlPiUKICBncm91cF9ieShnZW5kZXJfbGluayx3b3JrZXJpZCx0cmlhbF9nZW5kZXIsdHJpYWxfY29uZ3J1ZW5jeSkgJT4lCiAgc3VtbWFyaXNlKG1lYW5ydCA9IG1lYW4ocmVzaWRfcnQpKQogIApgYGAKCgpgYGB7cn0KYWdncl9zcGVha2VyICU+JQogIGdncGxvdChhZXMoeD1nZW5kZXJfbGluayx5PW1lYW5ydCxjb2xvcj10cmlhbF9nZW5kZXIsbGluZXR5cGU9dHJpYWxfY29uZ3J1ZW5jeSkpICsgCiAgZ2VvbV9wb2ludCgpICsgCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSdsbScpCmBgYAoKCgpgYGB7cn0KYWdnX3NwZWFrZXJfbWVhbl9nZW4gPC0gYWxsX2RhdGEgJT4lCiAgZ3JvdXBfYnkodHJpYWxfZ2VuZGVyLHdvcmtlcmlkKSAlPiUKICBzdW1tYXJpemUoTWVhblJUPW1lYW4ocmVzaWRfcnQpKQpgYGAKCmBgYHtyfQphbGxfZGF0YSAlPiUKICBncm91cF9ieSh0cmlhbF9nZW5kZXIpICU+JQogIHN1bW1hcml6ZShNZWFuUlQgPSBtZWFuKHJlc2lkX3J0KSwgQ0kuTG93ID0gY2kubG93KHJlc2lkX3J0KSwgQ0kuSGlnaCA9IGNpLmhpZ2gocmVzaWRfcnQpKSAlPiUKICBtdXRhdGUoWU1pbiA9IE1lYW5SVCAtIENJLkxvdywgWU1heCA9IE1lYW5SVCArIENJLkhpZ2gpICU+JQogIGdncGxvdChhZXMoeD10cmlhbF9nZW5kZXIseT1NZWFuUlQpKSArIAogIGdlb21fcG9pbnQoc2l6ZT0zKSArIAogIGdlb21faml0dGVyKGRhdGEgPSBhZ2dfc3BlYWtlcl9tZWFuX2dlbiwgYWVzKHk9TWVhblJUKSxhbHBoYT0uNCxjb2xvcj0nbWVkaXVtc2xhdGVibHVlJykgKyAKICBnZW9tX2Vycm9yYmFyKGFlcyh5bWluPVlNaW4seW1heD1ZTWF4KSwgd2lkdGg9LjI1KSAKYGBgCgpgYGB7cn0KYWxsX2RhdGEgJT4lCiAgZmlsdGVyKCFpcy5uYShwb2xpX3BhcnR5KSkgJT4lCiAgZ3JvdXBfYnkodHJpYWxfY29uZ3J1ZW5jeSxwb2xpX3BhcnR5KSAlPiUKICBzdW1tYXJpemUoTWVhblJUID0gbWVhbihyZXNpZF9ydCksIENJLkxvdyA9IGNpLmxvdyhyZXNpZF9ydCksIENJLkhpZ2ggPSBjaS5oaWdoKHJlc2lkX3J0KSkgJT4lCiAgbXV0YXRlKFlNaW4gPSBNZWFuUlQgLSBDSS5Mb3csIFlNYXggPSBNZWFuUlQgKyBDSS5IaWdoKSAlPiUKICBnZ3Bsb3QoYWVzKHg9dHJpYWxfY29uZ3J1ZW5jeSx5PU1lYW5SVCkpICsgCiAgZ2VvbV9wb2ludChzaXplPTMpICsgCiAgZ2VvbV9lcnJvcmJhcihhZXMoeW1pbj1ZTWluLHltYXg9WU1heCksIHdpZHRoPS4yNSkgKyAKICBmYWNldF93cmFwKH5wb2xpX3BhcnR5KQpgYGAKCmBgYHtyfQphbGxfZGF0YSAlPiUKICBmaWx0ZXIoIWlzLm5hKHBvbGlfcGFydHkpKSAlPiUKICBncm91cF9ieSh0cmlhbF9nZW5kZXIscG9saV9wYXJ0eSkgJT4lCiAgc3VtbWFyaXplKE1lYW5SVCA9IG1lYW4ocmVzaWRfcnQpLCBDSS5Mb3cgPSBjaS5sb3cocmVzaWRfcnQpLCBDSS5IaWdoID0gY2kuaGlnaChyZXNpZF9ydCkpICU+JQogIG11dGF0ZShZTWluID0gTWVhblJUIC0gQ0kuTG93LCBZTWF4ID0gTWVhblJUICsgQ0kuSGlnaCkgJT4lCiAgZ2dwbG90KGFlcyh4PXRyaWFsX2dlbmRlcix5PU1lYW5SVCkpICsgCiAgZ2VvbV9wb2ludChzaXplPTMpICsgCiAgZ2VvbV9lcnJvcmJhcihhZXMoeW1pbj1ZTWluLHltYXg9WU1heCksIHdpZHRoPS4yNSkgKyAKICBmYWNldF93cmFwKH5wb2xpX3BhcnR5KQpgYGAKCmBgYHtyfQpwb2xpX2RhdGEgPC0gYWxsX2RhdGEgJT4lIAogIGdyb3VwX2J5KHdvcmtlcmlkKSAlPiUKICBzdW1tYXJpc2UocGFydHkgPSBwYXN0ZSh1bmlxdWUocG9saV9wYXJ0eSkpKQpgYGAKCmBgYHtyfQp0YWJsZShwb2xpX2RhdGEkcGFydHkpCmBgYAoKYGBge3J9CnBvbGlfZGF0YV9ncmFuIDwtIGFsbF9kYXRhICU+JSAKICBncm91cF9ieSh3b3JrZXJpZCkgJT4lCiAgc3VtbWFyaXNlKHBhcnR5ID0gcGFzdGUodW5pcXVlKHN1YmplY3RfaW5mb3JtYXRpb24ucGFydHlfYWxpZ25tZW50KSkpCmBgYAoKYGBge3J9CnRhYmxlKHBvbGlfZGF0YV9ncmFuJHBhcnR5KQpgYGAKCiMgTW9kZWwgQW5hbHlzZXMgey50YWJzZXR9CgojIyBSZWFkaW5nIFRpbWUKCgpgYGB7cn0KYWxsX2RhdGEgPC0gYWxsX2RhdGEgJT4lCiAgbXV0YXRlKGN0cmlhbF9jb25ncnVlbmN5ID0gYXMubnVtZXJpYyhhcy5mYWN0b3IodHJpYWxfY29uZ3J1ZW5jeSkpLW1lYW4oYXMubnVtZXJpYyhhcy5mYWN0b3IodHJpYWxfY29uZ3J1ZW5jeSkpKSkgJT4lCiAgbXV0YXRlKGN0cmlhbF9nZW5kZXIgPSBhcy5udW1lcmljKGFzLmZhY3Rvcih0cmlhbF9nZW5kZXIpKS1tZWFuKGFzLm51bWVyaWMoYXMuZmFjdG9yKHRyaWFsX2dlbmRlcikpKSkgJT4lCiAgbXV0YXRlKGNnZW5kZXJfbGluayA9IHNjYWxlKGdlbmRlcl9saW5rKSkKYGBgCgoKCmBgYHtyfQpjb21wbGV4X21vZGVsIDwtIGxtZXIocmVzaWRfcnR+Y3RyaWFsX2NvbmdydWVuY3kqY3RyaWFsX2dlbmRlcipjZ2VuZGVyX2xpbmsgKyAoMXx3b3JrZXJpZCkgKyAoMXxsZXhlbWUpICsgKDF8bmFtZSksZGF0YSA9IGFsbF9kYXRhLGNvbnRyb2w9bG1lckNvbnRyb2wob3B0Q3RybD1saXN0KG1heGZ1bj00MDAwMCkpKQpgYGAKCgpgYGB7cn0Kc3VtbWFyeShjb21wbGV4X21vZGVsKQpgYGAKCmBgYHtyfQpwbG90KGNvbXBsZXhfbW9kZWwpCmBgYAoKCmBgYHtyfQpjb21wbGV4X21vZGVsX2JhcmUgPC0gbG1lcihyZXNpZF9ydH50cmlhbF9jb25ncnVlbmN5KnRyaWFsX2dlbmRlciArICgxICsgdHJpYWxfY29uZ3J1ZW5jeSArIHRyaWFsX2dlbmRlciB8IHdvcmtlcmlkKSArICgxfGxleGVtZSkgKyAoMXxuYW1lKSxkYXRhID0gYWxsX2RhdGEsY29udHJvbD1sbWVyQ29udHJvbChvcHRDdHJsPWxpc3QobWF4ZnVuPTIwMDAwKSkpCmBgYAoKYGBge3J9CnN1bW1hcnkoY29tcGxleF9tb2RlbF9iYXJlKQpgYGAKCgpgYGB7cn0KcGxvdChjb21wbGV4X21vZGVsKQpgYGAKYGBge3J9CmNvbXBsZXhfbW9kZWxfYW50aXJhbmRvbSA8LSBsbShyZXNpZF9ydH5jdHJpYWxfY29uZ3J1ZW5jeSpjdHJpYWxfZ2VuZGVyKmNnZW5kZXJfbGluaywgZGF0YSA9IGFsbF9kYXRhKQpgYGAKCmBgYHtyfQpwbG90KGNvbXBsZXhfbW9kZWxfYW50aXJhbmRvbSkKYGBgCgoKIyBFeHRyYXMKCiMjIENvbmZpcm1pbmcgdGhlIEdlbmRlciBTY2FsZXMKCmBgYHtyfQpub19nZW5kZXJsZXNzIDwtIGFsbF9kYXRhW2NvbXBsZXRlLmNhc2VzKGFsbF9kYXRhKSwgXQoKZ2VuZGVyX2NoZWNrIDwtIGxtKGdlbmRlcl90b3RhbH5zdWJqZWN0X2luZm9ybWF0aW9uLmdlbmRlcipwb2xpX3BhcnR5LCBkYXRhPW5vX2dlbmRlcmxlc3MpCmBgYAoKYGBge3J9CnN1bW1hcnkoZ2VuZGVyX2NoZWNrKQpgYGAKYGBge3J9CnBsb3QoZ2VuZGVyX2NoZWNrKQpgYGAKCiMjIENvbW1lbnRzIAoKYGBge3J9CnN1bW1hcnkoYWxsX2RhdGEkc3ViamVjdF9pbmZvcm1hdGlvbi5jb21tZW50cykKYGBgCmBgYHtyfQphbGxfZGF0YSAlPiUKICBncm91cF9ieSh3b3JrZXJpZCxwb2xpX3BhcnR5KSAlPiUKICBzdW1tYXJpc2UoY29tbWVudHMgPSBwYXN0ZSh1bmlxdWUoc3ViamVjdF9pbmZvcm1hdGlvbi5jb21tZW50cykpKSAlPiUKICBzZWxlY3QocG9saV9wYXJ0eSxjb21tZW50cykKYGBgCgpgYGB7cn0KYWdlX21vZGVsIDwtIGxtKHJlc2lkX3J0fnN1YmplY3RfaW5mb3JtYXRpb24uYWdlLCBkYXRhID0gYWxsX2RhdGEpCmBgYAoKYGBge3J9CnN1bW1hcnkoYWdlX21vZGVsKQpgYGAKCmBgYHtyfQpuZXdfdG95IDwtIHJlYWQuY3N2KCJtZXJnZWRfYWxsLmNzdiIpIApgYGAKCmBgYHtyfQpuZXdfdG95ICU+JQogIGZpbHRlcihwcm9saWZlcmF0ZS5jb25kaXRpb249PSJhbGwiKSAlPiUKICBzdW1tYXJ5KCkKYGBgCgo=